《Java 2D游戏编程入门》—— 2.3 处理鼠标输入

    xiaoxiao2024-05-21  101

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

    2.3 处理鼠标输入

    SimpleMouseInput类位于javagames.util包中,它和前面小节中开发的键盘输入类非常相似。处理鼠标按键的方式与处理键盘按键的方式相同。实现MouseListener接口的类包含了如下的方法:

    mouseClicked(MouseEvent e) mouseEntered(MouseEvent e) mouseExited(MouseEvent e) mousePressed(MouseEvent e) mouseReleased(MouseEvent e)``` 这个接口中的两个方法,mouseEntered()和mouseExited(),负责处理鼠标光标移动。另外3个方法用于鼠标按键。就像键盘监听器一样,按下和释放方法会记录鼠标按钮的状态,而点击方法则会被忽略。为了确定按下了哪个鼠标按键,鼠标事件包含了如下的方法:

    public int MouseEvent.getButton()`这个方法所返回的值,映射到如下的常量:

    MouseEvent.NOBUTTON = 0 MouseEvent.BUTTON1 = 1 MouseEvent.BUTTON2 = 2 MouseEvent.BUTTON3 = 3``` 按键的编号是从1开始而不是从0开始的,0表示没有按键,在引用鼠标按键数组的时候,不需要将按键编号减1。除了这一点小小的差别,鼠标按键和键盘按键的处理方式相同,也包括buttonDown()和buttonDownOnce()方法。 还有其他的鼠标状态可供程序使用,例如鼠标指针的位置和状态。和MouseListener接口的mouseEntered()和mouseExited()方法一样,鼠标输入类也实现了MouseMotionListener接口。

    mouseDragged(MouseEvent e)mouseMoved(MouseEvent e)`针对鼠标事件,如果鼠标进入或离开了组件监听,所有这4个方法(进入、退出、拖拽和移动)都会捕获鼠标的位置并通知程序。如下的方法会获取鼠标的当前位置:

    public Point MouseEvent.getPoint()``` 当轮询鼠标输入时,会复制这个值并使其可供游戏循环使用。当前的鼠标位置,对于如下的方法来说是可用的:

    public Point SimpleMouseInput.getPosition()`最后,为了监控鼠标滚轮的输入,输入类实现了MouseWheelListener:

    mouseWheelMoved(MouseEvent e)``` 如下的方法返回了鼠标滚轮的点击。如果这个数值是负值,表示滚轮已经从用户那里移走了。如果这个值是正的,表示滚轮已经朝着用户移动了。

    public int MouseWheelEvent.getWheelRotation()`这个值也保存在poll()方法中,并且可通过如下方法供游戏循环使用:

    public int SimpleMouseInput.getNotches()``` SimpleMouseInput类的代码如下:

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

    public class SimpleMouseInputimplements 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;  public SimpleMouseInput() {    mousePos = new Point( 0, 0 );    currentPos = new Point( 0, 0 );    mouse = new boolean[ BUTTON_COUNT ];    polled = new int[ BUTTON_COUNT ];  }  public synchronized void poll() {    mousePos = new Point( currentPos );    polledNotches = notches;    notches = 0;    for( int i = 0; i < mouse.length; ++i ) {      if( mouse[i] ) {        polled[i]++;      } else {        polled[i] = 0;      }    }  }  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 ) {    currentPos = e.getPoint();  }  public synchronized void mouseWheelMoved( MouseWheelEvent e ) {    notches += e.getWheelRotation();  }}`SimpleMouseExample如图2.2所示,它位于javagames.input包中,为第一个示例添加了很多新的内容。这个示例以主动渲染示例代码为基础,添加了键盘和鼠标输入类;它不仅展示了所有3种类型的鼠标输入,而且也是使用Graphics对象绘制到屏幕的第一个示例。

    首先需要注意的是,创建GUI时的方法调用。这些调用添加了KeyboardInput和Simple MouseInput作为组件的监听器。注意,尽管给JFrame和Canvas都添加了键盘,但是只给Canvas添加了鼠标。当应用程序初次启动时,如果没有给JFrame添加键盘事件的话,它不会处理键盘事件。一旦画布对象接受到焦点,它将会接受键盘输入,但在画布被选中之前,都只有JFrame接受键盘输入。在游戏循环中,添加了如下的方法调用:

    public void processInput()``` 在这个方法中,对键盘和鼠标都进行了轮询,以确保它们的数据可用。当初次按下鼠标按键时,会设置绘制标志。对于保持鼠标按键按下的每一帧,都会向该行的数据结构添加一个新的点。当按键释放的时候,该标志被清除,并且向列表添加一个空的对象,标志该行的结束。通过这种方式,该数据结构可以同时保存所有的行。 当按下C键的时候,该行从数据结构中清除。如果用户缺乏绘画技巧的话(就像我一样),这会允许他们重新开始。渲染方法也有新的代码加入。除了显示帧速率,还给出了使用该应用程序的说明,并把当前鼠标的位置显示为字符串。 鼠标滚轮用来选择一个颜色索引。如下的代码使用模除运算符和绝对值来保证索引是一个有效值,无论所获取的索引有多大或多小。

    // SimpleMouseExample.javacolorIndex += mouse.getNotches();Color color = COLORS[ Math.abs( colorIndex % COLORS.length ) ];g.setColor( color );`%操作符将会保证值在(–3, 3)之间。因为对一个负值进行模除运算,会得到0或者一个负值,所以使用绝对值来保证数组索引位于(0, size –1)之间。

    最后,绘制了线条。由于在数据结构中插入了空的值,我们添加了代码以确保没有点为空的时候才绘制线条。

    package javagames.input; import java.awt.*; import java.awt.event.*; import java.awt.image.*; import java.util.*; import javagames.util.*; import javax.swing.*; public class SimpleMouseExample extends JFrame          implements Runnable {   private FrameRate frameRate;   private BufferStrategy bs;   private volatile boolean running;   private Thread gameThread;   private SimpleMouseInput mouse;   private KeyboardInput keyboard;   private ArrayList<Point> lines = new ArrayList<Point>();   private boolean drawingLine;   private Color[] COLORS = {     Color.RED,     Color.GREEN,     Color.YELLOW,     Color.BLUE   };   private int colorIndex;   public SimpleMouseExample() {     frameRate = new FrameRate();   }   protected void createAndShowGUI() {     Canvas canvas = new Canvas();     canvas.setSize( 640, 480 );     canvas.setBackground( Color.BLACK );     canvas.setIgnoreRepaint( true );     getContentPane().add( canvas );     setTitle( "Simple Mouse Example" );     setIgnoreRepaint( true );     pack();     // Add key listeners     keyboard = new KeyboardInput();     canvas.addKeyListener( keyboard );     // Add mouse listeners     mouse = new SimpleMouseInput();     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();     if( keyboard.keyDownOnce( KeyEvent.VK_SPACE ) ) {       System.out.println("VK_SPACE");     }     // if button is pressed for first time,     // start drawing lines     if( mouse.buttonDownOnce( MouseEvent.BUTTON1 ) ) {       drawingLine = true;     }     // if the button is down, add line point     if( mouse.buttonDown( MouseEvent.BUTTON1 ) ) {       lines.add( mouse.getPosition() );       // if the button is not down but we were drawing,       // add a null to break up the lines     } else if( drawingLine ) {       lines.add( null );       drawingLine = false;     }     // if ’C’ is down, clear the lines     if( keyboard.keyDownOnce( KeyEvent.VK_C ) ) {       lines.clear();     }   }   private void render( Graphics g ) {     colorIndex += mouse.getNotches();     Color color = COLORS[ Math.abs( colorIndex % COLORS.length ) ];     g.setColor( color );     frameRate.calculate();     g.drawString( frameRate.getFrameRate(), 30, 30 );     g.drawString( "Use mouse to draw lines", 30, 45 );     g.drawString( "Press C to clear lines", 30, 60 );     g.drawString( "Mouse Wheel cycles colors", 30, 75 );     g.drawString( mouse.getPosition().toString(), 30, 90 );     for( int i = 0; i < lines.size() - 1; ++i ) {     Point p1 = lines.get( i );       Point p2 = lines.get( i + 1 );       // Adding a null into the list is used       // for breaking up the lines when       // there are two or more lines       // that are not connected       if( !( p1 == null || p2 == null ) )         g.drawLine( p1.x, p1.y, p2.x, p2.y );     }   }   protected void onWindowClosing() {     try {       running = false;       gameThread.join();     } catch( InterruptedException e ) {       e.printStackTrace();     }     System.exit( 0 );   }   public static void main( String[] args ) {     final SimpleMouseExample app = new SimpleMouseExample();     app.addWindowListener( new WindowAdapter() {       public void windowClosing( WindowEvent e ) {         app.onWindowClosing();       }     });     SwingUtilities.invokeLater( new Runnable() {       public void run() {         app.createAndShowGUI();       }     });   } 相关资源:敏捷开发V1.0.pptx
    最新回复(0)