交互界面与输入(2)

来源:互联网 发布:软件外包开发管理制度 编辑:程序博客网 时间:2024/05/21 00:28

3.6使用输入管理器:要使用输入管理器,首先建立一个简单的游戏,不过后面还要增加其功能,并让用户配置键盘。首先生成让英雄生成左右移动和跳动的游戏,还要增加暂停游戏的功能。
3.6.1暂停游戏:暂停游戏操纵实际发生情况主要有2点,首先不更新游戏对象和动画,忽略输入。为此,需要修改游戏的循环,在不暂停游戏的时候才可以检查输入和更新游戏对象,实现代码如下:
if(!pause){
checkInput();
updateGameObject();
}
其次,即使暂停游戏,屏幕也要继续绘图,甚至更多的内容。如用动画显示暂停消息等。另外,用户按P之类的键时,还要更新暂停状态。
3.6.2 增加重力:对待重力的幽灵,幽灵速度则根据加速度进行更新,实现代码如下:
velocityY=velocityY+GRAVITY*elapsedTime;地球的重力为9.8米/秒,但不适用于游戏中,可以用像素和毫秒作为单位,只要使用感觉合适即可。这里GRAVITY的值为 0.002,使游戏者可以跳动,但不会跳到屏幕以外。清单3.7中的player类增加了重力特性,player类是扩展的sprite类,不过是增加了重力特性和跳动功能。
清单 3.7 Player.java
import com.brackeen.javagamebook.graphics.*;

/**
    The Player extends the Sprite class to add states
    (STATE_NORMAL or STATE_JUMPING) and gravity.
*/
public class Player extends Sprite {

    public static final int STATE_NORMAL = 0;
    public static final int STATE_JUMPING = 1;

    public static final float SPEED = .3f;
    public static final float GRAVITY = .002f;

    private int floorY;
    private int state;

    public Player(Animation anim) {
        super(anim);
        state = STATE_NORMAL;
    }


    /**
        Gets the state of the Player (either STATE_NORMAL or
        STATE_JUMPING);
    */
    public int getState() {
        return state;
    }


    /**
        Sets the state of the Player (either STATE_NORMAL or
        STATE_JUMPING);
    */
    public void setState(int state) {
        this.state = state;
    }


    /**
        Sets the location of "floor", where the Player starts
        and lands after jumping.
    */
    public void setFloorY(int floorY) {
        this.floorY = floorY;
        setY(floorY);
    }


    /**
        Causes the Player to jump
    */
    public void jump() {
        setVelocityY(-1);
        state = STATE_JUMPING;
    }


    /**
        Updates the player's positon and animation. Also, sets the
        Player's state to NORMAL if a jumping Player landed on
        the floor.
    */
    public void update(long elapsedTime) {
        // set vertical velocity (gravity effect)
        if (getState() == STATE_JUMPING) {
            setVelocityY(getVelocityY() + GRAVITY * elapsedTime);
        }

        // move player
        super.update(elapsedTime);

        // check if player landed on floor
        if (getState() == STATE_JUMPING && getY() >= floorY) {
            setVelocityY(0);
            setY(floorY);
            setState(STATE_NORMAL);
        }

    }
}
Player类共有2个状态:NORMAL与JMPING。由于Player的跟踪能力,所以在检查过程中保证只在正常状态下执行跳动,达到跳动时才增加重力。此外,还要告诉player 对象发生的地点,用setFloorY()方法来设置。这样所有的元素都已经实现,那么就可以生成一个简单的游戏---InputManager对象,它的代码清单如下。InputManagerTest类可以让用户移动游戏者并跳动。利用几个GameAction对象来提供这些功能。每个GameAction至少贴图一个键或鼠标事件,甚至包括暂停游戏。若要移动游戏者,可以用箭头方向键。若要游戏者跳动,可以用空格键。p键用来暂停游戏,而Escape键用来退出游戏。
清单  InputManagerTest.java

import java.awt.*;
import java.awt.event.KeyEvent;

import com.brackeen.javagamebook.graphics.*;
import com.brackeen.javagamebook.input.*;
import com.brackeen.javagamebook.test.GameCore;

/**
    InputManagerTest tests the InputManager with a simple
    run-and-jump mechanism. The player moves and jumps using
    the arrow keys and the space bar.
    <p>Also, InputManagerTest demonstrates pausing a game
    by not updating the game elements if the game is paused.
*/
public class InputManagerTest extends GameCore {

    public static void main(String[] args) {
        new InputManagerTest().run();
    }

    protected GameAction jump;
    protected GameAction exit;
    protected GameAction moveLeft;
    protected GameAction moveRight;
    protected GameAction pause;
    protected InputManager inputManager;
    private Player player;
    private Image bgImage;
    private boolean paused;

    public void init() {
        super.init();
        Window window = screen.getFullScreenWindow();
        inputManager = new InputManager(window);

        // use these lines for relative mouse mode
        //inputManager.setRelativeMouseMode(true);
        //inputManager.setCursor(InputManager.INVISIBLE_CURSOR);

        createGameActions();
        createSprite();
        paused = false;
    }


    /**
        Tests whether the game is paused or not.
    */
    public boolean isPaused() {
        return paused;
    }


    /**
        Sets the paused state.
    */
    public void setPaused(boolean p) {
        if (paused != p) {
            this.paused = p;
            inputManager.resetAllGameActions();
        }
    }


    public void update(long elapsedTime) {
        // check input that can happen whether paused or not
        checkSystemInput();

        if (!isPaused()) {
            // check game input
            checkGameInput();

            // update sprite
            player.update(elapsedTime);
        }
    }


    /**
        Checks input from GameActions that can be pressed
        regardless of whether the game is paused or not.
    */
    public void checkSystemInput() {
        if (pause.isPressed()) {
            setPaused(!isPaused());
        }
        if (exit.isPressed()) {
            stop();
        }
    }


    /**
        Checks input from GameActions that can be pressed
        only when the game is not paused.
    */
    public void checkGameInput() {
        float velocityX = 0;
        if (moveLeft.isPressed()) {
            velocityX-=Player.SPEED;
        }
        if (moveRight.isPressed()) {
            velocityX+=Player.SPEED;
        }
        player.setVelocityX(velocityX);

        if (jump.isPressed() &&
            player.getState() != Player.STATE_JUMPING)
        {
            player.jump();
        }
    }


    public void draw(Graphics2D g) {
        // draw background
        g.drawImage(bgImage, 0, 0, null);

        // draw sprite
        g.drawImage(player.getImage(),
            Math.round(player.getX()),
            Math.round(player.getY()),
            null);
    }


    /**
        Creates GameActions and maps them to keys.
    */
    public void createGameActions() {
        jump = new GameAction("jump",
            GameAction.DETECT_INITAL_PRESS_ONLY);
        exit = new GameAction("exit",
            GameAction.DETECT_INITAL_PRESS_ONLY);
        moveLeft = new GameAction("moveLeft");
        moveRight = new GameAction("moveRight");
        pause = new GameAction("pause",
            GameAction.DETECT_INITAL_PRESS_ONLY);

        inputManager.mapToKey(exit, KeyEvent.VK_ESCAPE);
        inputManager.mapToKey(pause, KeyEvent.VK_P);

        // jump with spacebar or mouse button
        inputManager.mapToKey(jump, KeyEvent.VK_SPACE);
        inputManager.mapToMouse(jump,
            InputManager.MOUSE_BUTTON_1);

        // move with the arrow keys...
        inputManager.mapToKey(moveLeft, KeyEvent.VK_LEFT);
        inputManager.mapToKey(moveRight, KeyEvent.VK_RIGHT);

        // ... or with A and D.
        inputManager.mapToKey(moveLeft, KeyEvent.VK_A);
        inputManager.mapToKey(moveRight, KeyEvent.VK_D);

        // use these lines to map player movement to the mouse
        //inputManager.mapToMouse(moveLeft,
        //  InputManager.MOUSE_MOVE_LEFT);
        //inputManager.mapToMouse(moveRight,
        //  InputManager.MOUSE_MOVE_RIGHT);

    }


    /**
        Load images and creates the Player sprite.
    */
    private void createSprite() {
        // load images
        bgImage = loadImage("../images/background.jpg");
        Image player1 = loadImage("../images/player1.png");
        Image player2 = loadImage("../images/player2.png");
        Image player3 = loadImage("../images/player3.png");

        // create animation
        Animation anim = new Animation();
        anim.addFrame(player1, 250);
        anim.addFrame(player2, 150);
        anim.addFrame(player1, 150);
        anim.addFrame(player2, 150);
        anim.addFrame(player3, 200);
        anim.addFrame(player2, 150);

        player = new Player(anim);
        player.setFloorY(screen.getHeight() - player.getHeight());
    }

}
3.7 使用Swing组件
3.8.1 Swing 基础:大多数Swing 组件很容易生成,都是从基类JComponent派生出来的。下面要讲okButton对象添加到屏幕中,从而实现单击功能。组件添加到上层容器中,这里的上层容器就是JFrame对象,下面的示例将一个按钮添加到内容窗格中:
JFrame frame=screen.getFullScreenWindow();
frame.getContentPane().add(okButton);
将一个按钮加到内容窗格中,但缺省情况下按钮只放在屏幕左上角。可以有两种方法解决。第一种方法是用okButton.setLocation(200,200)显式布置很容易实现,但并不完美,假设2个按钮要靠在一起,则可能放到不同系统中时,缺省Swing外观可能生成更大的按钮,从而造成重叠或很难看的样子。第二种方法是使用布局管理器。布局管理器考虑每个组件的尺寸,根据一定规则布置组件。布局管理器有很多种,而且会用不同方法布置组件,如FlowLayOut布局管理器按从左到右,从上到下方式在屏幕上布置组件。要使用布局管理器,可以调用内容窗格的setLayout()方法,下面是使用FlowLayout布局管理器的实例,实现代码如下:
frame.getContentPane().setLayout(new FlowLayout());
3.8.2 在全屏方式下使用Swing
要绘制所有Swing组件,只要用paintComponents()方法画出分层窗格中的内容,这是在动画循环中完成的,实现代码如下:
draw(g);
JFrame frame=screen.getFullScreenWindow();
frame.getLayeredPane().paintComponents(g);
代码中反映出有2个问题:第一,内容窗格实际绘制背景,会隐藏其下面的一切,所以要让Swing组件放在自己的图形上方。这个问题很容易解决,只要让内容窗格透明,实现代码如下:
if(contentPane instanceof JComponet){
((JComponent)contentPane).setOpaque(false);
}
第二个问题与Swing组件绘制的方法有关,在正常Swing应用程序中,不必调用paintComponents()方法,Swing会自动在AWT事件派生线程中进行绘制工作。但是,这里没有关掉这个功能,所以在组件改变外观时,组件会调用repaint()方法,以请求重画。要解决这个问题就要捕获重画请求和将其忽略。所有重画请求都要发送到RepaintManager对象上,它管理请求并转发,实际绘制组件。只要生成NullRepaintManager覆盖这个功能即可。如清单3.9所示,NullRepaintManager只是让重画请求消失。
清单 3.9 NullRepaintManager.java
package com.brackeen.javagamebook.graphics;

import javax.swing.RepaintManager;
import javax.swing.JComponent;

/**
    The NullRepaintManager is a RepaintManager that doesn't
    do any repainting. Useful when all the rendering is done
    manually by the application.
*/
public class NullRepaintManager extends RepaintManager {

    /**
        Installs the NullRepaintManager.
    */
    public static void install() {
        RepaintManager repaintManager = new NullRepaintManager();
        repaintManager.setDoubleBufferingEnabled(false);
        RepaintManager.setCurrentManager(repaintManager);
    }

    public void addInvalidComponent(JComponent c) {
        // do nothing
    }

    public void addDirtyRegion(JComponent c, int x, int y,
        int w, int h)
    {
        // do nothing
    }

    public void markCompletelyDirty(JComponent c) {
        // do nothing
    }

    public void paintDirtyRegions() {
        // do nothing
    }

}
这样就可以了,要安装NullRepaintManager只要调用NullRepaintManager.install()方法即可。
3.9创建简单菜单
现在可以扩展InputManagerTest对象中的游戏,在简单的用户界面加上暂停,配置,和退出按钮。虽然目前配置按钮什么也不干,但是要先把它放到屏幕上。首先确定单击按钮时会发生什么情形,当Swing发现单击事件,并检查按钮是否有任何ActionListeners.如果有,则在AWT事件派生线程中告诉这些ActionListeners已经单击按钮。与KeyListener和MouseListener接口一样,ActionListener也是任何对象都可以实现的接口,它有个actionPerformed()方法,并取ActionEvent作为参数。可以用ActionEvent的getSource()方法检查事件是哪个组件生成,实现代码如下:
public void actionPerformed(ActionEvent e){
object src=e.getSource();
if(src==okButton){
}
}
最后,用户界面中可以对按钮进行些处理使其更有用。
(1)增加工具提示。
(2)使用图标。
(3)隐藏缺省外观。要让图标单独显示,因此关掉按钮边框,并用setContentAreaFilled(false)方法保证按钮不绘制按钮背景。
(4) 改变光标。
(5)关掉焦点。
这些改变都在MenuTest类的createButton()方法中完成。清单3.10 MenuTest类扩展InputManagerTes类,并增加几个按钮。
清单 3.10 MenuTest.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

import com.brackeen.javagamebook.graphics.*;
import com.brackeen.javagamebook.input.GameAction;

/**
    Extends the InputManagerTest demo and adds Swing buttons
    for pause, config and quit.
*/
public class MenuTest extends InputManagerTest
    implements ActionListener
{

    public static void main(String[] args) {
        new MenuTest().run();
    }

    protected GameAction configAction;

    private JButton playButton;
    private JButton configButton;
    private JButton quitButton;
    private JButton pauseButton;
    private JPanel playButtonSpace;

    public void init() {
        super.init();
        // make sure Swing components don't paint themselves
        NullRepaintManager.install();

        // create an addtional GameAction for "config"
        configAction = new GameAction("config");

        // create buttons
        quitButton = createButton("quit", "Quit");
        playButton = createButton("play", "Continue");
        pauseButton = createButton("pause", "Pause");
        configButton = createButton("config", "Change Settings");

        // create the space where the play/pause buttons go.
        playButtonSpace = new JPanel();
        playButtonSpace.setOpaque(false);
        playButtonSpace.add(pauseButton);

        JFrame frame = super.screen.getFullScreenWindow();
        Container contentPane = frame.getContentPane();

        // make sure the content pane is transparent
        if (contentPane instanceof JComponent) {
            ((JComponent)contentPane).setOpaque(false);
        }

        // add components to the screen's content pane
        contentPane.setLayout(new FlowLayout(FlowLayout.LEFT));
        contentPane.add(playButtonSpace);
        contentPane.add(configButton);
        contentPane.add(quitButton);

        // explicitly layout components (needed on some systems)
        frame.validate();
    }


    /**
        Extends InputManagerTest's functionality to draw all
        Swing components.
    */
    public void draw(Graphics2D g) {
        super.draw(g);
        JFrame frame = super.screen.getFullScreenWindow();

        // the layered pane contains things like popups (tooltips,
        // popup menus) and the content pane.
        frame.getLayeredPane().paintComponents(g);
    }


    /**
        Changes the pause/play button whenever the pause state
        changes.
    */
    public void setPaused(boolean p) {
        super.setPaused(p);
        playButtonSpace.removeAll();
        if (isPaused()) {
            playButtonSpace.add(playButton);
        }
        else {
            playButtonSpace.add(pauseButton);
        }
    }


    /**
        Called by the AWT event dispatch thread when a button is
        pressed.
    */
    public void actionPerformed(ActionEvent e) {
        Object src = e.getSource();
        if (src == quitButton) {
            // fire the "exit" gameAction
            super.exit.tap();
        }
        else if (src == configButton) {
            // doesn't do anything (for now)
            configAction.tap();
        }
        else if (src == playButton || src == pauseButton) {
            // fire the "pause" gameAction
            super.pause.tap();
        }
    }


    /**
        Creates a Swing JButton. The image used for the button is
        located at "../images/menu/" + name + ".png". The image is
        modified to create a "default" look (translucent) and a
        "pressed" look (moved down and to the right).
        <p>The button doesn't use Swing's look-and-feel and
        instead just uses the image.
    */
    public JButton createButton(String name, String toolTip) {

        // load the image
        String imagePath = "../images/menu/" + name + ".png";
        ImageIcon iconRollover = new ImageIcon(imagePath);
        int w = iconRollover.getIconWidth();
        int h = iconRollover.getIconHeight();

        // get the cursor for this button
        Cursor cursor =
            Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);

        // make translucent default image
        Image image = screen.createCompatibleImage(w, h,
            Transparency.TRANSLUCENT);
        Graphics2D g = (Graphics2D)image.getGraphics();
        Composite alpha = AlphaComposite.getInstance(
            AlphaComposite.SRC_OVER, .5f);
        g.setComposite(alpha);
        g.drawImage(iconRollover.getImage(), 0, 0, null);
        g.dispose();
        ImageIcon iconDefault = new ImageIcon(image);

        // make a pressed iamge
        image = screen.createCompatibleImage(w, h,
            Transparency.TRANSLUCENT);
        g = (Graphics2D)image.getGraphics();
        g.drawImage(iconRollover.getImage(), 2, 2, null);
        g.dispose();
        ImageIcon iconPressed = new ImageIcon(image);

        // create the button
        JButton button = new JButton();
        button.addActionListener(this);
        button.setIgnoreRepaint(true);
        button.setFocusable(false);
        button.setToolTipText(toolTip);
        button.setBorder(null);
        button.setContentAreaFilled(false);
        button.setCursor(cursor);
        button.setIcon(iconDefault);
        button.setRolloverIcon(iconRollover);
        button.setPressedIcon(iconPressed);

        return button;
    }

}
3,10 让游戏者配置键盘
这个特性可以使用户将任何的键盘和鼠标事件贴图到任何游戏动作上。用户只要选择游戏动作并按一个键或单击鼠标,即可把这个键指定给游戏的动作。键盘配置特性可以分为2个部分:
(1)首先要实际生成Cofiguration对话框
(2)然后要生成一个特殊组件,让用户输入键或鼠标单击。
生成的组件称为InputComponent,是清单3.11中KeyConfigTest类的内类。
InputComponent是JTextField的子类。尽管用户可以通过正常JTextField组件传入任意普通文本,但这里只让用户输入键或鼠标单击,因此要覆盖JTextField的输入方法。通常,生成KeyListener与MouseListener接口以监听输入事件,但这时要用不同方法取键,当然取得输入事件方法有多种。每个Swing组件都是Component类的一个实例。Component类具有processKeyEvent()和processMouseEvent()之类的方法。这些方法与KeyListener和MouseListener中的方法相似。只要覆盖这些方法,用enableEvents()方法启用输入事件即可。
清单3.11 KeyConfigTest.java
import java.awt.event.*;
import java.awt.*;
import java.util.List;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.border.Border;

import com.brackeen.javagamebook.graphics.*;
import com.brackeen.javagamebook.input.*;

/**
    The KeyConfigTest class extends the MenuTest demo to add
    a dialog to configure the keyboard keys.
*/
public class KeyConfigTest extends MenuTest {

    public static void main(String[] args) {
        new KeyConfigTest().run();
    }

    private static final String INSTRUCTIONS =
        "<html>Click an action's input box to change it's keys." +
        "<br>An action can have at most three keys associated " +
        "with it.<br>Press Backspace to clear an action's keys.";

    private JPanel dialog;
    private JButton okButton;
    private List inputs;

    public void init() {
        super.init();

        inputs = new ArrayList();

        // create the list of GameActions and mapped keys
        JPanel configPanel = new JPanel(new GridLayout(5,2,2,2));
        addActionConfig(configPanel, moveLeft);
        addActionConfig(configPanel, moveRight);
        addActionConfig(configPanel, jump);
        addActionConfig(configPanel, pause);
        addActionConfig(configPanel, exit);

        // create the panel containing the OK button
        JPanel bottomPanel = new JPanel(new FlowLayout());
        okButton = new JButton("OK");
        okButton.setFocusable(false);
        okButton.addActionListener(this);
        bottomPanel.add(okButton);

        // create the panel containing the instructions.
        JPanel topPanel = new JPanel(new FlowLayout());
        topPanel.add(new JLabel(INSTRUCTIONS));

        // create the dialog border
        Border border =
            BorderFactory.createLineBorder(Color.black);

        // create the config dialog.
        dialog = new JPanel(new BorderLayout());
        dialog.add(topPanel, BorderLayout.NORTH);
        dialog.add(configPanel, BorderLayout.CENTER);
        dialog.add(bottomPanel, BorderLayout.SOUTH);
        dialog.setBorder(border);
        dialog.setVisible(false);
        dialog.setSize(dialog.getPreferredSize());

        // center the dialog
        dialog.setLocation(
            (screen.getWidth() - dialog.getWidth()) / 2,
            (screen.getHeight() - dialog.getHeight()) / 2);

        // add the dialog to the "modal dialog" layer of the
        // screen's layered pane.
        screen.getFullScreenWindow().getLayeredPane().add(dialog,
            JLayeredPane.MODAL_LAYER);
    }


    /**
        Adds a label containing the name of the GameAction and an
        InputComponent used for changing the mapped keys.
    */
    private void addActionConfig(JPanel configPanel,
        GameAction action)
    {
        JLabel label = new JLabel(action.getName(), JLabel.RIGHT);
        InputComponent input = new InputComponent(action);
        configPanel.add(label);
        configPanel.add(input);
        inputs.add(input);
    }


    public void actionPerformed(ActionEvent e) {
        super.actionPerformed(e);
        if (e.getSource() == okButton) {
            // hides the config dialog
            configAction.tap();
        }
    }


    public void checkSystemInput() {
        super.checkSystemInput();
        if (configAction.isPressed()) {
            // hide or show the config dialog
            boolean show = !dialog.isVisible();
            dialog.setVisible(show);
            setPaused(show);
        }
    }


    /**
        Resets the text displayed in each InputComponent, which
        is the names of the mapped keys.
    */
    private void resetInputs() {
        for (int i=0; i<inputs.size(); i++) {
            ((InputComponent)inputs.get(i)).setText();
        }
    }


    /**
        The InputComponent class displays the keys mapped to a
        particular action and allows the user to change the mapped
        keys. The user selects an InputComponent by clicking it,
        then can press any key or mouse button (including the
        mouse wheel) to change the mapped value.
    */
    class InputComponent extends JTextField  {

        private GameAction action;

        /**
            Creates a new InputComponent for the specified
            GameAction.
        */
        public InputComponent(GameAction action) {
            this.action = action;
            setText();
            enableEvents(KeyEvent.KEY_EVENT_MASK |
                MouseEvent.MOUSE_EVENT_MASK |
                MouseEvent.MOUSE_MOTION_EVENT_MASK |
                MouseEvent.MOUSE_WHEEL_EVENT_MASK);
        }


        /**
            Sets the displayed text of this InputComponent to the
            names of the mapped keys.
        */
        private void setText() {
            String text = "";
            List list = inputManager.getMaps(action);
            if (list.size() > 0) {
                for (int i=0; i<list.size(); i++) {
                    text+=(String)list.get(i) + ", ";
                }
                // remove the last comma
                text = text.substring(0, text.length() - 2);
            }

            // make sure we don't get deadlock
            synchronized (getTreeLock()) {
                setText(text);
            }

        }


        /**
            Maps the GameAction for this InputComponent to the
            specified key or mouse action.
        */
        private void mapGameAction(int code, boolean isMouseMap) {
            if (inputManager.getMaps(action).size() >= 3) {
                inputManager.clearMap(action);
            }
            if (isMouseMap) {
                inputManager.mapToMouse(action, code);
            }
            else {
                inputManager.mapToKey(action, code);
            }
            resetInputs();
            screen.getFullScreenWindow().requestFocus();
        }


        // alternative way to intercept key events
        protected void processKeyEvent(KeyEvent e) {
            if (e.getID() == e.KEY_PRESSED) {
                // if backspace is pressed, clear the map
                if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE &&
                    inputManager.getMaps(action).size() > 0)
                {
                    inputManager.clearMap(action);
                    setText("");
                    screen.getFullScreenWindow().requestFocus();
                }
                else {
                    mapGameAction(e.getKeyCode(), false);
                }
            }
            e.consume();
        }


        // alternative way to intercept mouse events
        protected void processMouseEvent(MouseEvent e) {
            if (e.getID() == e.MOUSE_PRESSED) {
                if (hasFocus()) {
                    int code = InputManager.getMouseButtonCode(e);
                    mapGameAction(code, true);
                }
                else {
                    requestFocus();
                }
            }
            e.consume();
        }


        // alternative way to intercept mouse events
        protected void processMouseMotionEvent(MouseEvent e) {
            e.consume();
        }


        // alternative way to intercept mouse events
        protected void processMouseWheelEvent(MouseWheelEvent e) {
            if (hasFocus()) {
                int code = InputManager.MOUSE_WHEEL_DOWN;
                if (e.getWheelRotation() < 0) {
                    code = InputManager.MOUSE_WHEEL_UP;
                }
                mapGameAction(code, true);
            }
            e.consume();
        }
    }
}
KeyConfigTest类扩展MenuTest类。Init()方法生成配置对话框将其加进分层窗格。checkSystemInput()方法覆盖成检查配置按钮是否按下,如果是,则显示配置对话框。InputComponent有几个方法可以取得输入,并可以用 InputManager类的方法将键和鼠标事件贴图到游戏动作。另外,InputComponent类将游戏动作可以贴图的键数限制为3个。这是认为的限制,实际中InputManager没有限制游戏动作可以贴图的键数。另外,InputComponent把退格键作为特殊键。按下退格键时,清除游戏动作的所有贴图键。如果游戏动作没有贴图键,则贴图退格键。还有一点没有提到是,JLables可以包含HTML。这里可以用HTML把包含指令的Jlable分解为多行,这是用HTML的分行标志<br>实现的。