《Java 2D游戏编程入门》—— 2.4 相对鼠标移动

    xiaoxiao2024-05-15  112

    本节书摘来异步社区《Java 2D游戏编程入门》一书中的第2章,第2.4节,作者:【美】Timothy Wright(莱特),更多章节内容可以访问云栖社区“异步社区”公众号查看。

    2.4 相对鼠标移动

    对于图2.3所示的当前的鼠标输入类来说,有一个问题。首先,它看上去似乎挺明显,但是,只有在鼠标位于窗口之中的时候,程序才接受鼠标事件。一旦鼠标离开了窗口,鼠标指针的坐标位置在应用程序中就变得不再有效。更为糟糕的是,在全屏模式中,当鼠标到达屏幕边缘的时候,它会直接停下来,而不会继续注册事件。根据应用程序的需要,可能需要相对鼠标移动。好在,更新鼠标输入类以支持相对鼠标移动并不难。

    RelativeMouseInput类位于javagames.util包中,构建于前面示例中的类的基础之上,并且添加了相对鼠标移动。为了实现这一点,用Robot类来将鼠标保持在窗口的中央。还有监听鼠标事件的Swing组件,可以计算窗口的中央位置,并且从相对窗口坐标转换为绝对屏幕坐标。如果鼠标光标总是位于窗口的中央,那么,它可能不会离开,并且窗口将总是接受鼠标事件。如下代码保持鼠标居中:

    // RelativeMouseInput.java private Point getComponentCenter() {   int w = component.getWidth();   int h = component.getHeight();   return new Point( w / 2, h / 2 ); } private void centerMouse() {   if( robot != null && component.isShowing() ) {     Point center = getComponentCenter();     SwingUtilities.convertPointToScreen( center, component );     robot.mouseMove( center.x, center.y );   } }``` 只有在运行时才会计算窗口的中央位置,因此即使窗口改变了大小,鼠标将仍然位于中央。相对中央位置必须转换为绝对屏幕坐标。不管窗口位于桌面上的何处,窗口左上角的像素都是(0,0)。如果在桌面上移动窗口会改变左上角像素的值,那么,图形化编程将会变得非常困难。尽管使用相对像素值会使得绘制较为容易,但把鼠标定位到窗口的中央并没有考虑窗口的位置,也没有考虑到将鼠标光标放置到距离窗口很远时,它会停止接受鼠标事件。使用SwingUtilities类转换得到屏幕坐标,从而解决这一问题。 要意识到将鼠标重新居中很重要,因为当鼠标的新位置和当前位置相同时,要求Robot类重新把鼠标定位到相同的位置,而这并不会产生新的鼠标事件。如果这种行为有变化的话,相对鼠标类将总是产生鼠标事件,即便鼠标并没有移动。即便鼠标行为将来不发生变化,在请求重新居中之前,也应先检查当前位置和新的位置是否不同,这种做法将会解决这个问题。 给RelativeMouseInput类添加一个标志,以允许相对和绝对鼠标移动。一款游戏在某些时候可能既需要绝对鼠标模式,也需要相对鼠标模式,因此,这个类允许在运行时切换。mouseMoved()方法添加了新的代码。如果是在相对模式中,距离将计算为与中心点之间的差距,然后让鼠标光标重新居中。由于鼠标坐标和组件坐标都是相对值,因此不需要转换这些值。最后,在轮询方法的过程中,鼠标位置可以是相对的或绝对的。在轮询方法中,delta变量和所有其他变量一起重新设置。

    package javagames.util;import java.awt.*;import java.awt.event.*;import javax.swing.*;

    public class RelativeMouseInputimplements MouseListener, MouseMotionListener, MouseWheelListener {  private static final int BUTTON_COUNT = 3;  private Point mousePos;  private Point currentPos;  private boolean[] mouse;  private int[] polled;  private int notches;  private int polledNotches;  private int dx, dy;  private Robot robot;  private Component component;  private boolean relative;  public RelativeMouseInput( Component component ) {    this.component = component;    try {      robot = new Robot();    } catch( Exception e ) {      // Handle exception [game specific]      e.printStackTrace();    }    mousePos = new Point( 0, 0 );    currentPos = new Point( 0, 0 );    mouse = new boolean[ BUTTON_COUNT ];    polled = new int[ BUTTON_COUNT ];  }  public synchronized void poll() {    if( isRelative() ) {      mousePos = new Point( dx, dy );    } else {      mousePos = new Point( currentPos );    }    dx = dy = 0;    polledNotches = notches;    notches = 0;    for( int i = 0; i < mouse.length; ++i ) {      if( mouse[i] ) {        polled[i]++;      } else {        polled[i] = 0;      }    }  }  public boolean isRelative() {    return relative;  }  public void setRelative( boolean relative ) {    this.relative = relative;    if( relative ) {      centerMouse();    }  }  public Point getPosition() {    return mousePos;  }  public int getNotches() {    return polledNotches;  }  public boolean buttonDown( int button ) {    return polled[ button - 1 ] > 0;  }  public boolean buttonDownOnce( int button ) {    return polled[ button - 1 ] == 1;  }  public synchronized void mousePressed( MouseEvent e ) {    int button = e.getButton() - 1;    if( button >= 0 && button < mouse.length ) {      mouse[ button ] = true;    }  }  public synchronized void mouseReleased( MouseEvent e ) {    int button = e.getButton() - 1;    if( button >= 0 && button < mouse.length ) {      mouse[ button ] = false;    }  }  public void mouseClicked( MouseEvent e ) {    // Not needed  }  public synchronized void mouseEntered( MouseEvent e ) {    mouseMoved( e );  }  public synchronized void mouseExited( MouseEvent e ) {    mouseMoved( e );  }  public synchronized void mouseDragged( MouseEvent e ) {    mouseMoved( e );  }  public synchronized void mouseMoved( MouseEvent e ) {    if( isRelative() ) {      Point p = e.getPoint();      Point center = getComponentCenter();      dx += p.x - center.x;      dy += p.y - center.y;      centerMouse();    } else {      currentPos = e.getPoint();    }  }  public synchronized void mouseWheelMoved( MouseWheelEvent e ) {    notches += e.getWheelRotation();  }  private Point getComponentCenter() {    int w = component.getWidth();    int h = component.getHeight();    return new Point( w / 2, h / 2 );  }  private void centerMouse() {    if( robot != null && component.isShowing() ) {      Point center = getComponentCenter();      SwingUtilities.convertPointToScreen( center, component );      robot.mouseMove( center.x, center.y );    }  }}`RelativeMouseExample位于javagames.input包中,如图2.4所示,它针对鼠标输入类测试更新的新功能。

    如下的代码,通过将光标图像设置为在运行时创建的一个空的光标,从而关闭鼠标光标。

    // RelativeMouseExample .java private void disableCursor() {   Toolkit tk = Toolkit.getDefaultToolkit();   Image image = tk.createImage( "" );   Point point = new Point( 0, 0 );   String name = "CanBeAnything";   Cursor cursor = tk.createCustomCursor( image, point, name );   setCursor( cursor ); }``` 这个示例的render()方法显示了帮助文本,计算了帧速率,并且在屏幕上绘制了矩形。在processInput()方法内部,空格将鼠标模式从绝对模式切换为相对模式。C键用来显示或隐藏鼠标光标。在当前的鼠标位置用来更新方块位置的时候,相对值添加到了该位置,而绝对值替代了之前的值。这段代码确保了方框不会离开屏幕,如果它在任何一个方向走得太远的话,都会折返其位置。

    package javagames.input;import java.awt.*;import java.awt.event.*;import java.awt.image.*;import javagames.util.*;import javax.swing.*;

    public class RelativeMouseExample extends JFrame          implements Runnable {  private FrameRate frameRate;  private BufferStrategy bs;  private volatile boolean running;  private Thread gameThread;  private Canvas canvas;  private RelativeMouseInput mouse;  private KeyboardInput keyboard;  private Point point = new Point( 0, 0 );  private boolean disableCursor = false;  public RelativeMouseExample() {    frameRate = new FrameRate();  }  protected void createAndShowGUI() {    canvas = new Canvas();    canvas.setSize( 640, 480 );    canvas.setBackground( Color.BLACK );    canvas.setIgnoreRepaint( true );    getContentPane().add( canvas );    setTitle( "Relative Mouse Example" );    setIgnoreRepaint( true );    pack();    // Add key listeners    keyboard = new KeyboardInput();    canvas.addKeyListener( keyboard );    // Add mouse listeners    // For full screen : mouse = new RelativeMouseInput( this );    mouse = new RelativeMouseInput( canvas );    canvas.addMouseListener( mouse );    canvas.addMouseMotionListener( mouse );    canvas.addMouseWheelListener( mouse );    setVisible( true );    canvas.createBufferStrategy( 2 );    bs = canvas.getBufferStrategy();    canvas.requestFocus();    gameThread = new Thread( this );    gameThread.start();  }  public void run() {    running = true;    frameRate.initialize();    while( running ) {      gameLoop();    }  }  private void gameLoop() {    processInput();    renderFrame();    sleep( 10L );  }  private void renderFrame() {    do {      do {        Graphics g = null;        try {          g = bs.getDrawGraphics();          g.clearRect( 0, 0, getWidth(), getHeight() );          render( g );        } finally {          if( g != null ) {            g.dispose();          }        }      } while( bs.contentsRestored() );      bs.show();    } while( bs.contentsLost() );  }  private void sleep( long sleep ) {    try {      Thread.sleep( sleep );    } catch( InterruptedException ex ) { }  }  private void processInput() {    keyboard.poll();    mouse.poll();    Point p = mouse.getPosition();    if( mouse.isRelative() ) {      point.x += p.x;      point.y += p.y;    } else {      point.x = p.x;      point.y = p.y;    }    // Wrap rectangle around the screen    if( point.x + 25 < 0 )      point.x = canvas.getWidth() - 1;    else if( point.x > canvas.getWidth() - 1 )      point.x = -25;    if( point.y + 25 < 0 )      point.y = canvas.getHeight() - 1;    else if( point.y > canvas.getHeight() - 1 )      point.y = -25;    // Toggle relative    if( keyboard.keyDownOnce( KeyEvent.VK_SPACE ) ) {      mouse.setRelative( !mouse.isRelative() );    }    // Toggle cursor    if( keyboard.keyDownOnce( KeyEvent.VK_C ) ) {      disableCursor = !disableCursor;      if( disableCursor ) {        disableCursor();      } else {        // setCoursor( Cursor.DEFAULT_CURSOR ) is deprecated        setCursor( new Cursor( Cursor.DEFAULT_CURSOR ) );      }    }  }  private void render( Graphics g ) {    g.setColor( Color.GREEN );    frameRate.calculate();    g.drawString( mouse.getPosition().toString(), 20, 20 );    g.drawString( "Relative: " + mouse.isRelative(), 20, 35 );    g.drawString( "Press Space to switch mouse modes", 20, 50 );    g.drawString( "Press C to toggle cursor", 20, 65 );    g.setColor( Color.WHITE );    g.drawRect( point.x, point.y, 25, 25 );  }  private void disableCursor() {    Toolkit tk = Toolkit.getDefaultToolkit();    Image image = tk.createImage( "" );    Point point = new Point( 0, 0 );    String name = "CanBeAnything";    Cursor cursor = tk.createCustomCursor( image, point, name );    setCursor( cursor );  }  protected void onWindowClosing() {    try {      running = false;      gameThread.join();    } catch( InterruptedException e ) {      e.printStackTrace();    }    System.exit( 0 );  }  public static void main( String[] args ) {    final RelativeMouseExample app = new RelativeMouseExample();    app.addWindowListener( new WindowAdapter() {      public void windowClosing( WindowEvent e ) {        app.onWindowClosing();      }    });    SwingUtilities.invokeLater( new Runnable() {      public void run() {        app.createAndShowGUI();      }    });  }}`

    相关资源:Java 游戏编程开发教程中文高清完整版PDF
    最新回复(0)