本節書摘來異步社群《java 2d遊戲程式設計入門》一書中的第2章,第2.4節,作者:【美】timothy wright(萊特),更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。
對于圖2.3所示的目前的滑鼠輸入類來說,有一個問題。首先,它看上去似乎挺明顯,但是,隻有在滑鼠位于視窗之中的時候,程式才接受滑鼠事件。一旦滑鼠離開了視窗,滑鼠指針的坐标位置在應用程式中就變得不再有效。更為糟糕的是,在全屏模式中,當滑鼠到達螢幕邊緣的時候,它會直接停下來,而不會繼續注冊事件。根據應用程式的需要,可能需要相對滑鼠移動。好在,更新滑鼠輸入類以支援相對滑鼠移動并不難。
relativemouseinput類位于javagames.util包中,建構于前面示例中的類的基礎之上,并且添加了相對滑鼠移動。為了實作這一點,用robot類來将滑鼠保持在視窗的中央。還有監聽滑鼠事件的swing元件,可以計算視窗的中央位置,并且從相對視窗坐标轉換為絕對螢幕坐标。如果滑鼠光标總是位于視窗的中央,那麼,它可能不會離開,并且視窗将總是接受滑鼠事件。如下代碼保持滑鼠居中:
package javagames.util;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class relativemouseinput
implements 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 ) {
mouse[ button ] = false;
public void mouseclicked( mouseevent e ) {
// not needed
public synchronized void mouseentered( mouseevent e ) {
mousemoved( e );
public synchronized void mouseexited( mouseevent e ) {
public synchronized void mousedragged( mouseevent e ) {
public synchronized void mousemoved( mouseevent e ) {
point p = e.getpoint();
point center = getcomponentcenter();
dx += p.x - center.x;
dy += p.y - center.y;
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() ) {
swingutilities.convertpointtoscreen( center, component );
robot.mousemove( center.x, center.y );
}<code>`</code>
relativemouseexample位于javagames.input包中,如圖2.4所示,它針對滑鼠輸入類測試更新的新功能。
如下的代碼,通過将光标圖像設定為在運作時建立的一個空的光标,進而關閉滑鼠光标。
package javagames.input;
import java.awt.image.*;
import javagames.util.*;
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 ) {
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;
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();
// 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() {
running = false;
gamethread.join();
} catch( interruptedexception e ) {
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();