Now that you have an idea about the role that user input plays
in games, you're ready to learn the specifics of how user input
works in Java. This lesson is devoted to the handling of user
input in Java, including the supported input devices and the methods
used to trap input events. You also learn about the event-driven
structure of Java event handling and how it applies to input events.
This lesson clearly establishes the fact that Java user input
handling is both powerful and simple. A handful of methods is
really all it takes to deal with user input in a Java game. You
get to see this firsthand by building a Java applet with keyboard
and mouse input support. Let's get started.
Before getting into the details of how user input is handled in
Java, it's important that you understand how event-driven programming
works. This is important because user input in Java is heavily
based on the event-driven architecture that makes up the heart
of Java. In Java, an event is defined quite literally as
something that happens that you might want to know about. For
example, when a Java component gains the input focus, an event
occurs because it might be important for your applet to know about
the focus change.
An event is something that happens that you might want
to know about and possibly react to.
In the event-driven world of Java, the flow of your program follows
events external to your applet, as opposed to following an internally
linear program flow. This is an important point because it means
that a Java applet is in a constant state of responding to events.
The most visible events are things such as mouse clicks and key
presses, which are known as input events. You provide methods
that respond to these events, which are called event handlers.
An input event is an event generated by user manipulation
of an input device such as the mouse or keyboard.
Event handlers are special methods that are used to respond
or react to events.
Because of the inherent graphical nature of Java applets, it will
eventually become obvious to you why the event-driven programming
model is not only more convenient, but downright necessary. With
the potential of having multiple applets on a single Web page,
along with on-the-fly system configuration changes and a multitude
of other things going on, a procedural programming model would
be much more difficult to manage. The event-based model provides
a more sound solution to the problems inherent in a system with
a graphical interface, such as Java.
All events in Java are processed within the awt windowing toolkit
package, and they are tightly linked to awt components. A component
is basically a generic abstraction for a Java window. You might
recall that Java applets are themselves a specific type of component,
which means that they inherit the same event-processing features
built into the Component
superclass.
A component is a generic abstraction of a window in Java.
As you just learned, user input in Java is handled through an
event-driven architecture. When the user interacts with an input
device, it results in an input event being dispatched to the component
with the input focus. In most cases, this component is the applet
window. An input event is a special type of event that notifies
an applet that something has occurred on an input device. An example
of an input event is a movement of the mouse.
Input events are crucial in Java games because they provide a
means of handling user responses. Without being able to monitor
user responses, Java games wouldn't be too exciting. User response
handling is not only important for providing an interface to the
user for playing a game; it also establishes much of the feel
of a game. Simply altering the means by which you provide user
response support can dramatically alter the play of a game. This
is an important point, one that you'll deal with later in this
lesson when you develop the Flying Saucer sample applet.
Java user event responses come in two varieties, which correspond
to the input devices supported by Java. The two types of input
events supported by release 1.0 of Java are as follows:
Keyboard events
Mouse events
Keyboard events are events generated by a key press on the keyboard.
Any time the user presses a key, a keyboard event is generated
that can be trapped and handled by the applet with the input focus.
Actually, a key press generates two events: a key down event and
a key up event. You'll learn more about these two types soon.
Mouse events are generated by mouse clicks and movements. Every
mouse click and mouse movement generates a corresponding mouse
input event. Like key presses, mouse clicks actually come as a
series of events: a mouse click down event and a mouse click up
event. A mouse event is also specifically targeted at mouse dragging.
Mouse dragging occurs when the mouse is moved with the button
down. Applets that want to respond to mouse clicks and movement
simply have to process these events and take action accordingly.
You learn more about processing mouse events a little later in
this lesson.
Note
You might have noticed in the discussion of mouse events the mention of the mouse button, as opposed to the mouse buttons. This is intentional because Java only supports a single mouse button. This might seem limiting to users on some
platforms, such as Windows, but keep in mind that Java is designed to support as many platforms as possible. Considering the fact that some platforms (such as Macintosh) have mice with a single button, it makes sense for Java to support only a single
button.
Before getting into the specific event handlers for keyboard and
mouse events, let's look at how events are handled in a general
sense in Java. The Java awt provides the Event
class for encapsulating all types of events that can occur within
the system. The Event class
models a generic event and has constants defined within it to
represent specific events. The Event
class is used primarily by the handleEvent
method of Component. The
handleEvent method is the
default event handler for all events, and is defined as follows:
public boolean handleEvent(Event evt)
Notice that handleEvent takes
an Event object as its only
parameter. handleEvent uses
this Event object to determine
what type of event has occurred. It then calls a more specific
event handler method to deal with the specific event. For example,
if a key is pressed, the Event
object's id member variable
is set to KEY_PRESS, which
is a constant defining the key press event. handleEvent
checks the value of id and,
upon finding it equal to KEY_PRESS,
calls the keyDown handler
method. Listing 9.1 shows the key press handling portion of the
handleEvent method in the
1.0 release of Java.
Listing 9.1. The key press portion of the handleEvent
method.
public boolean handleEvent(Event evt)
{
switch (evt.id) {
...
case Event.KEY_PRESS:
return keyDown(evt, evt.key);
...
}
return false;
}
The handling of other system events is very similar to that of
the KEY_PRESS event. You
could easily override handleEvent
to provide custom routing of event handlers, but it is rarely
necessary. Although you might not ever need to intervene with
the default event handling provided by handleEvent,
it is nevertheless important to understand how it works.
Java keyboard events are generated when the user presses or releases
a key. Two standard keyboard event handler methods are supported
by the Component class: keyDown
and keyUp. These two methods
are defined as follows:
public boolean keyDown(Event evt, int
key)
public boolean keyUp(Event evt, int key)
The keyDown method is called
in response to the user pressing a key, and the keyUp
method is called in response to the user releasing a key. Both
methods are passed an Event
object and an integer key value parameter. The key value parameter,
key, specifies which key
was pressed or released. The Event
object parameter contains extra information relating to the keyboard
event, such as whether the Shift key was held down when the key
was pressed.
The Event object contains
constants representing the different keys that can be specified
in the key parameter. Table
9.1 shows a list of the more useful key constants.
Table 9.1. Useful key constants for games.
Constant
Key
UP
Up arrow
DOWN
Down arrow
LEFT
Left arrow
RIGHT
Right arrow
HOME
Home
END
End
PGUP
Page Up
PGDN
Page Down
To check whether the key pressed or released is one of these keys,
you override keyDown or keyUp
and compare the value of key
to one of the constants. Listing 9.2 contains an example of overriding
keyDown to check for the
user pressing one of the arrow keys.
Listing 9.2. Handling arrow key presses in the keyDown
method.
public boolean keyDown(Event evt, int
key) {
switch (key) {
case Event.LEFT:
// left arrow key pressed
break;
case Event.RIGHT:
// right arrow key pressed
break;
case Event.UP:
// up arrow key pressed
break;
case Event.DOWN:
// down arrow key pressed
break;
}
return true;
}
This keyDown method shows
that handling different key presses is as easy as providing a
switch statement with case
clauses for each key. Although the example here used the keyDown
method for handling key presses, the keyUp
method works in the same fashion.
If you need more details about the key that was pressed or released,
you can use the Event object
passed into the keyDown and
keyUp methods. The typical
usage of the Event object
in regard to key processing is to check for modifier keys. Modifier
keys are keys that can be pressed in conjunction with other input
events, such as the Shift and Control keys. The following are
the three methods in Event
used to check the status of modifier keys:
public boolean shiftDown()
public boolean controlDown()
public boolean metaDown()
All of these methods return boolean values specifying whether
or not the key in question is being held down. Checking the status
of the modifier keys is necessary sometimes in applets that make
heavy use of the mouse. For example, you might have a drawing
applet that performs a different function if the Shift key is
held down and the mouse is moved. You probably won't need the
modifier keys in Java games, but it is still important to know
how they work. Who knows, you might think of an interesting way
to incorporate them into a game.
Mouse events occur when the user moves the mouse or clicks the
mouse button. A handful of methods exist for handling mouse events,
such as the following methods:
public boolean mouseUp(Event evt, int
x, int y)
public boolean mouseDown(Event evt, int x, int y)
public boolean mouseMove(Event evt, int x, int y)
public boolean mouseDrag(Event evt, int x, int y)
public boolean mouseEnter(Event evt, int x, int y)
public boolean mouseExit(Event evt, int x, int y)
All of these methods are passed an Event
object and two integer parameters representing the X and Y position
of the mouse pointer. The mouseUp
and mouseDown methods are
called when the user presses and releases the mouse button. The
mouseMove method is called
when the mouse is moved. The mouseDrag
method is very similar to the mouseMove
method-the only difference being that mouseDrag
is called when the mouse is moved with the button held down. The
mouseEnter and mouseExit
methods are used to track when the mouse enters and exits the
applet window.
You can use the x and y
parameters passed into the mouse event handler methods to perform
any processing based on the position of the mouse. The following
code snippet contains an example of overriding the mouseMove
method to output the mouse position to standard output:
public boolean mouseMove(Event evt, int
x, int y) {
System.out.println("Mouse position = ("
+ x + ", " + y + ")");
return true;
}
Similar to the keyboard event handlers, you can use the Event
object passed in the mouse event handlers to find out additional
information such as the status of modifier keys.
Now that you have a good idea of how to process user input events
in Java, let's take a look at a sample applet that uses this newfound
knowledge. The Flying Saucer applet uses the sprite classes and
event handler methods to implement a user-controllable flying
saucer. Figure 9.1 shows what Flying Saucer looks like. The complete
source code, executable, and images for the Flying Saucer applet
are included on the accompanying CD-ROM.
The FlyingSaucer class models
the applet itself and takes care of all the details related to
setting up the sprite classes and handling the user input events.
Similar to the other applet classes you've developed that use
sprites, FlyingSaucer contains
familiar support for the sprite classes. In fact, the only significantly
new code in the FlyingSaucer
class is the code for handling user input.
Before getting into the specifics of the user input handlers,
however, take a look at two of the member variables defined in
the FlyingSaucer class:
private Sprite theSaucer;
private int lastKey;
The theSaucer member variable
is a Sprite object that holds
the flying saucer sprite. It is necessary to keep up with this
sprite outside of the sprite list because you need to be able
to alter its position and velocity based on user input events.
The lastKey member variable
is used to hold the value of the last key pressed. This variable
is used to provide finer control over the flying saucer, as you'll
see later in this lesson.
The keyDown method handles
all the details of supporting keyboard control of the saucer.
Listing 9.3 shows the source code for the keyDown
method.
Listing 9.3. The FlyingSaucer
class's keyDown
method.
public boolean keyDown(Event evt, int
key) {
// Change the saucer velocity based on the key pressed
Point vel = theSaucer.getVelocity();
switch (key) {
case Event.LEFT:
vel.x = -4;
if (lastKey == Event.LEFT)
vel.y = 0;
break;
case Event.RIGHT:
vel.x = 4;
if (lastKey == Event.RIGHT)
vel.y = 0;
break;
case Event.UP:
vel.y = -4;
if (lastKey == Event.UP)
vel.x = 0;
break;
case Event.DOWN:
vel.y = 4;
if (lastKey == Event.DOWN)
vel.x = 0;
break;
default:
vel.x = vel.y = 0;
}
theSaucer.setVelocity(vel);
lastKey = key;
return true;
}
The keyDown method first
gets the current velocity of the saucer and checks to see which
key was pressed. It then alters the saucer's velocity according
to the directional arrow key pressed. The lastKey
member variable is then checked to see whether this is a repeat
key press. If so, the tangential velocity component is cleared.
For example, if the left arrow key is held down, the Y velocity
component is cleared. This has the result of causing the saucer
to change from moving in a diagonal direction to moving in a pure
X or Y direction if you hold a key down, which gives the keyboard
controls a better feel. Try it out for yourself.
The mouseDown and mouseDrag
methods are used to handle mouse input events and position the
saucer at an absolute location:
public boolean mouseDown(Event evt, int
x, int y) {
theSaucer.setPosition(new Point(x - (saucerSize.width
/ 2),
y - (saucerSize.height / 2)));
return true;
}
public boolean mouseDrag(Event evt, int x, int y) {
theSaucer.setPosition(new Point(x - (saucerSize.width
/ 2),
y - (saucerSize.height / 2)));
return true;
}
Both of these methods simply reposition the saucer at a location
centered on the current mouse position, which enables you to click
and drag the saucer around with the mouse. This might not be an
ideal usage of the mouse in most game scenarios, but it shows
how the mouse can be used to control a sprite, which can be useful.
Note
You might be thinking that the duplicate code in the mouseDown and mouseDrag methods goes against good programming practice. You're right! The truth is that I didn't want to confuse
things by having these methods call a third method, which is typically the way you avoid duplicate code in a situation like this. You might also think that mouseDrag could just call mouseDown and simply pass its parameters along. Although this technique would work in this particular case, it's generally not a good idea to directly call event handler methods yourself, primarily because the Event parameter means different things to different event handlers.
In this lesson, you learned about Java events and the event-driven
architecture necessary to support them. More specifically, you
learned about Java input events, including the input devices capable
of generating them and how they are handled by the Java awt library.
You saw examples of using the input event handler methods to capture
and respond to keyboard and mouse events. You then finished up
the lesson with a sample applet using the sprite classes that
implements a user-controllable flying saucer supporting both keyboard
and mouse input. This sample applet brought you yet another step
closer to implementing a complete game. As a matter of fact, you're
now ready to embark on your first complete Java game; the next
lesson focuses on developing your first full-blown Java game,
Traveling Gecko.
What's the big deal with event-driven programming?
A
Event-driven programming provides a powerful methodology for handling the complexities inherent in a graphical system. By modeling every action in the system as an event with a corresponding handler, the
complexities are broken down into individually serviceable items.
Q
What are Java input events?
A
They are any events generated by the user manipulating an input device such as the mouse or keyboard.
Q
What is the purpose of the handleEvent method?
A
The handleEvent method acts as a router method for all events. All events must pass through handleEvent, which in turn calls the
appropriate event handler method.
Q
If the user has more than one mouse button, can I detect when the user presses one of the extra buttons?
A
No, Java only supports single-button mice. This is to eliminate the creation of extra button features that wouldn't be available to users (such as Macintosh users) with one button on their mice.
The Workshop section provides questions and exercises to help
strengthen your grasp on the material you learned toChapter. Try to
answer the questions and at least put some thought into the exercises
before moving on to tomorrow's lesson. You'll find the answers
to the questions in appendix A, "Quiz Answers."
Think of some things that can take place in a Java applet
that might generate events.
Think of some popular games and how the user input is handled
for each. Do they support the mouse? If so, how?
Add a hyperspace feature to the Flying Saucer applet. A hyperspace
feature would result in the saucer moving to a random location
if a certain key, such as the spacebar, is pressed.
Change the saucer in the Flying Saucer applet to a frame-animated
sprite.