Chapter 18 -- Serious Play: Game Applets
Chapter 18
Serious Play: Game Applets
CONTENTS
"What's your game?" Maybe that should be, "What's
your definition of a game?" Games play an important role
in our society. But what are games?
There are common components to all games: a goal, rules, and a
world (or a framework) in which the game is played. The goal of
a game drives the players to perform or to achieve. In chess,
the goal is to capture the opponent's King. The rules, in a way,
are impediments to achieving goals instantly. Often, the rules
make the game fair so that anyone playing the game has the same
chance at winning. In tic-tac-toe, for example, one rule is that
players must alternate turns; otherwise, one player easily would
dominate the game and win. The world in which games are played
provides a place where both the goal and the rules make sense;
it is the ground on which the struggle for the goal can be enacted.
As the rules always require, to succeed at the game, players must
remain in that world. The world of Monopoly, for example, is a
microcosm of the business world, and the players' goal is to achieve
financial domination. Players must travel around this world searching
for business prospects, acquiring properties, and becoming wealthy.
They are given the neighborhoods, currency, and power to achieve
that goal. Sometimes these components (goal, rules, world) are
difficult to identify, but in varying forms, they always exist.
For every game, when players know the rules, understand the world,
and want the goal, they can build strategies they can use to play
each game.
The ability to build strategies is important for every aspect
of life, so this chapter takes the viewpoint that games are a
testing ground for life skills. Consider the game of chess as
a testing ground for strategy, concentration, and sequencing;
the game of poker focuses on the skills of computing probability,
managing stress, and judging other players' reactions and behaviors.
This viewpoint gives you a common starting point.
What makes a good game? Good games have innumerable qualities
to them, as diverse as people's varying talents, and just as impossible
to list with any accuracy or completeness. Qualities that make
a game good engage players on the following levels:
Achievement-challenge/improvement
Adrenaline-risk/chance/excitement
Relations-social/wit/interplay
Senses-sensual/auditory/tactile
Creativity-imagination/allusion/aesthetic
A game that is rich with these attributes can be a good game only
if this richness is balanced with approachability. Players then
can experience the game's richness because it is accessible. This
chapter examines some examples of games and their various attributes.
How do these ideas about the concept of games relate to computers?
The first step is to examine the medium. Computers are good at
manipulating and massaging information, but they have very little
tactile input and diminished human interaction. A good computer
game must take all this into account. In Solitaire, for example,
there is no human interaction, and shuffling and redealing are
time consuming. Solitaire therefore is a good example of an ideal
game to be computerized. Although the tactile handling of the
cards is lost, most people prefer the efficiency of computer shuffling
and redealing.
Instead of computerizing an existing game, many games have been
designed especially for computers. Descent, a fully 3-D battle
game, makes wonderful use of the computer medium. Specifically,
the game uses the computer's capability to manipulate 3-D information.
Any other medium would have great difficulty creating the world
of Descent. A problem with Descent, however, is that it has a
complex interface. This detracts from its popularity.
The game Lemmings, on the other hand, makes innovative use of
the computer medium with an approachable interface. The concepts
in Lemmings break new ground with interface and game design. Players
guide a group of lemmings through a maze-like world by assigning
a series of jobs to individual lemmings who aide the others in
completing the maze. Most other contemporary computer game designs
focus on manipulating only one object for success, whereas Lemmings'
focus is on a group's success, possibly at the expense of a few
individuals. The innovation in the interface is the player's ability
to control multiple individuals within one group.
Now that home computers have the capacity to use the Internet,
there are many innovative possibilities for games. An existing
board game that would greatly benefit from a networked computer
is Diplomacy, which reenacts the First World War. The players
role-play the leaders of important European countries. Because
there is no element of chance to the game, players must conspire
with other leaders to achieve their individual goals of European
domination. Unfortunately, while playing the board game, the players
often are aware of who is talking to whom. This greatly affects
the trust factor in agreements. Imagine the game now with the
assistance of the net's anonymity. Conspiracy and backroom politics
are handled with secrecy. Much like the Solitaire example, this
game shows how a game can be enhanced by a new medium.
A good example of existing net games is multiuser dungeons (MUDs).
These let multiple people interact in one game world. Because
these games are mainly text based, they appeal to individuals'
imaginations, much like radio programs did before the advent of
television. Unfortunately, even though the text in the game can
be very appealing for imaginative purposes, the text-as an interface
to move around and interact with the world-also detracts from
the game because it is complex and cumbersome for beginners to
use.
Looking at games now as a designer/programmer, the key concept
is the way in which players interact with the world: the interface.
A difficult interface makes a game less approachable and also
can diminish the fun of play, especially if a lot of the difficulty
of the game lies in the interface and not in the content of the
game.
A good interface is intuitive and responsive. A good way to test
whether an interface is intuitive is to have some noncomputer
people play the game with minimal prior instruction, and then
see whether they can figure it out. One thing that helps people
figure things out is to ensure that the game is responsive to
input.
Just as you saw games as a training ground for life skills, a
good way to look at computer-game design is as a training ground
for interface-design skills. Computers are information tools;
the easier it is to use the tool to process the information into
a more desirable form, the better the tool is. In the creation
of a game, the programmer is free to design innovative interfaces
without having the burden of conventional or real-world problems.
Considering the richness of games, a revolution in interface design
probably will spring from a game interface.
The CD-ROM included with this book contains an example of a game
I developed that has a good interface. The game is called CopyCat,
and it, too, has a goal, rules, and a world. The goal of the game
is to copy a picture created by several patterned faces of a cube
or some other shape. The one rule is that players can slide or
roll the shape around only in specific ways. Here, the interface
plays a central role, because the complexity of the game springs
from the way players must manipulate the shape. The world consists
of the picture to be copied, a blank frame where the copy of the
picture will be, a shape with the patterned faces with which to
make the copy, some regions where the shape can be rolled, and
a duplicate shape used for reference.
To start off, you will look at the overall workings of the program.
The crucial step in programming a game is to be able to identify
the objects that are needed; it is about pattern recognition and
being able to break down the program into objects by searching
for commonalties. Finding the essence of the program often takes
a bit of work and usually requires abstract thinking. The pieces
I broke the CopyCat game
into follow:
CopyCat
PlayArea
Board
BoardBox
GroupGraph
TFSolid
Pieces also include your old friends from the preceding chapter:
Rotator
Solid
Omatrix
CopyCat is the applet, and
it is the foreman of the program; it gets all the other objects
going and then checks on them when needed. PlayArea
is a Canvas where the important
actions are performed. Most of your interest will be focused on
this class. Board is a class
that holds all the important information about the Board-the
picture to be replicated. BoardBox
is the Canvas that displays
the desired picture. The GroupGraph
holds the geometrical information about the shape/solid. TFSolid
stands for Top-Face-Solid,
which is a subclass of Solid
with special importance to the top face of the solid or shape.
Figure 18.1 shows an overview of how those pieces fit together
(after everything is instantiated by CopyCat).
I suggest that you play the game before reading the rest of this
chapter.
Figure 18.1 : The flow of CopyCat.
Three runnable objects are in this program: CopyCat,
PlayArea, and Rotator.
You already saw what Rotator
does, so now take a look at the other two objects. This system
actually is event driven, with the threads sleeping most of the
time. You can think of CopyCat
as a lazy foreman who snoozes at his desk and only wakes up when
PlayArea tells him that there
has been some event that may be interesting-something has changed.
He deals with the event and promptly goes back to sleep. Similarly,
PlayArea works on this event-driven
idea, except he is more like a fast food cashier because he gets
a lot of events: mouse down,
mouse up, and mouse
drag. He receives all his events through the event
handler. The separate thread in PlayArea
runs animations during the play; these are used to place the top
face of the shape on the game Board.
I will go into detail about how this all works, but for now, get
a feel for the data flow.
Notice that Rotator and BoardBox
are separate from the rest of the program. Rotator
is used to display a duplicate of the shape for reference. BoardBox
is responsible for displaying the goal-the picture to be copied.
Both BoardBox and Rotator
are there only to provide static information about the game; the
shape doesn't change midway through the game and neither does
the goal. So after they are instantiated, they have no contact
with the rest of the program. Figure 18.2 shows the visible components
of CopyCat: PlayArea,
Rotator, and BoardBox.
Figure 18.2 : The visible components of CopyCat:PlayArea, Rotator and BoardBox.
Now look at how CopyCat and
PlayArea communicate. It
is a simple interaction. CopyCat
wants to sleep until PlayArea
has changedThings. Both CopyCat
and PlayArea use Board.
Board stores the current
state of the Board; PlayArea
changes the current state of the Board
every time a top face of the shape is placed. Then CopyCat
simply checks to see whether the Board
is finished by seeing whether Board.finished
is true. In CopyCat's
run method, shown in Listing
18.1, the line PlayArea.thingsChange();
is really a question: "Have things changed?" The answer
to the question is always yes, but that is because PlayArea
waits for the answer to be yes before it responds to the request.
After CopyCat waits for things
to change, it checks to see whether something important has changed.
If the number of rotations has changed from what it was before,
it updates rotations and
displays it to the screen by RecordScore.
If the player has completed the pattern, it displays the appropriate
Finished message and plays
a little tune.
Listing 18.1. The run
method from CopyCat.
/* - - - - - - - - - - - - - - - - - - &nbs
p;- - - - */
public void run(){
rotations=0;
while(true){
/* .. wait for things
to change .. */
PlayArea.thingsChange();
if (rotations != PlayArea.rotations){
rotations = PlayArea.rotations;
RecordScore(rotations);
}
if (Board.finished &&
!finished){
finished = true;
subtitle.setBackground(new
Color(255,150,150));
if (level1)
subtitle.title=(String)
"Finished ("+rotations+"/"+Board.minRot+")";
else
subtitle.title=(String)
"Finished ("+rotations+"/?)";
purrr.play();
subtitle.centre();
subtitle.repaint();
}
}
}
thingsChange works like a
door at a high-school dance, where the organizers want the same
number of girls and boys to enter. The organizers ensure this
by only accepting a girl, then a boy, then a girl, and so on.
So if the last person let in was a boy, the next boy to come in
must wait for a girl to enter. Keeping this analogy in mind, take
a look at the code. Suppose that when the variable nochange
equals false, it indicates
"a girl was the last person let in." When nochange
is true, it indicates "a
boy was the last person let in." The question thingsChange
is like the boy, and changedThings
is like the girl. When CopyCat
calls PlayArea.thingsChange();,
it checks whether the last person admitted was a boy (nochange
is true); if so, then it
waits/sleeps. If, while CopyCat
is sleeping, someone wakes the boy, he checks to see whether a
girl has gone through. Eventually, a girl will go through and
the boy will be let in. After going in, the nochange
variable must be set to false,
which shows that the last person through was a boy. Just in case
a girl was waiting to come in, this variable wakes up everyone.
As it turns out, the counterpart changedThings
is exactly the same (see Listing 18.2).
Listing 18.2. The run
method from PlayArea.java.
/* - - - - - - - - - - - - - - - - - - &nbs
p;- - - - */
public synchronized void thingsChange() {
while (nochange == true)
try{ wait(); } catch (InterruptedException
e){}
nochange = true;
notifyAll();
}
/* - - - - - - - - - - - - - - - - - - -&
nbsp; - - -
*/
public synchronized void changedThings() {
while (nochange == false)
try{ wait(); } catch (InterruptedException
e){}
nochange = false;
notifyAll();
}
How does PlayArea deal with
its events? Now that you've finished with the interaction between
PlayArea and CopyCat,
you start to see that PlayArea
is the component that does the real work.
First, you must identify all the events PlayArea
knows how to deal with. As in Rotator,
mentioned in the preceding chapter, the events dealt with are
mouse down, mouse drag, and mouse up. Rotator's
methods are much simpler than these, so I encourage you to revisit
that chapter if this next section gets a bit too convoluted; a
reasonable understanding of Rotator's
event handling will be very helpful. It is important to understand
the overall goal for the interface so that you can use it as a
torch to light your way through some complicated code.
The goal is to have a responsive control to move the shape. The
mouse down event is interpreted as an attempt to grab the object.
The mouseDown will
be successful if the pointer is relatively close to
the shape; otherwise, nothing happens. If the shape is under the
control of the mouse, the mouseDrag
makes sense. This mouseDrag
method is fairly intuitive. Dragging the shape simply slides the
shape to where the mouse points. The tricky part occurs when the
mouse drags the shape over an oriented rough surface. These
rough surfaces make the shape roll in a particular direction.
The rolling behaves like a ratchet. During a roll, the shape locks
into the desired set positions and doesn't allow the user to go
back to the previous locked position. The desired position usually
has one of the faces pointing upright; this is referred to as
the top face. The mouse up event is interpreted as letting
go of the shape. The only tricky part to mouseUp
is that if the shape is let go in a place on the Board
where the top face will fit, the mouseUp
method calls up a small animation that shows how the top face
is placed on the Board. If
this interface outline is a little fuzzy, I suggest that you play
the game and pay close attention to the interface.
Now you can break down each of the three methods that handle the
interface. The next section starts with the easiest: mouseDown.
mouseDown
In the mouseDown method,
the fast-food cashier handles the event as long as an animation
isn't running and if the pointer is close enough to the shape.
As you will see later, all events are ignored if an animation
is running. If the mouse down event is handled, to indicate to
the player that the shape now is being held, the shape is raised;
the height of the shape becomes
slightly bigger. Also, the onSolid
flag is set to true. This
tells mouseDrag that the
shape is within the control of the mouse (see Listing 18.3).
Listing 18.3. The method mouseDown
from PlayArea.
/* - - - - - - - - - - - - - - - - - - &nbs
p;- - - - */
public boolean mouseDown(java.awt.Event evt, int x,
int y) {
/* .. pick up the solid .. */
if (!animation_running){
if (Math.abs(x-solid.cenx)<solid.W
&& Math.abs(y-solid.ceny)<solid.H){
onSolid = true;
height =
0.2;
return true;
}
}
return false;
}
mouseDrag
Unfortunately, all the handling of events isn't so simple. The
next event handler for PlayArea
is mouseDrag, shown in Listing
18.4. If not for the rolling part, the interface would be easy.
On the other hand, the rolling is the most important part of the
interface and the program.
The drag has several behaviors or modes: TRANSLATE,
IN_A, and IN_B.
The mode used depends on whether the mouse is over one of the
rough surfaces. The TRANSLATE
mode is the default mode. Being in this mode means that the object
will only slide, because it is not on a rough surface. The orientation
of the shape will stay fixed. Being in the IN_A
and IN_B modes means that
the mouse is over one of the rough surfaces. Dragging the shape
over either of the rough surfaces might make the shape roll over.
There are two different modes because, depending on which of the
two regions the mouse is over, the shape rolls in different directions.
Because this method is very long, I'll explain it in small pieces
at a time. Listing 18.4 shows the method, along with commentary
inserted between sections of code. I'll leave the mathematical
explanations to you, because a discussion of this would bog down
an already convoluted method.
Listing 18.4. The method mouseDrag
from PlayArea.
/* - - - - - - - - - - - - - - - - - - - &nb
sp;- - - */
public boolean mouseDrag(java.awt.Event evt, int x,
int y) {
/* .. move the around the solid -- maybe
roll it .. */
if (onSolid && !animation_running){
solid.cenx = x;
solid.ceny = y;
First, to perform the drag, the player must be holding the shape:
onSolid must be true.
The other condition is that the animations aren't running, so
the variable animation_running
is false. The next two lines
center the shape to where the mouse now is pointing.
/* .. find
the newMode--TRANSLATE, IN_A, or IN_B
this
is used to find out whether the mode has changed .. */
newMode = TRANSLATE;
for (int i=0;i<2;i++){
int chX = (int)((solid.cenx-region[i][0])*vec[i][0]+
(solid.ceny-region[i][1])*vec[i][1]);
int chY = (int)((solid.cenx-region[i][0])*vec[i][1]-
(solid.ceny-region[i][1])*vec[i][0]);
if (chX>=0
&& chX<region[i][2] &&
chY>=0
&& chY<region[i][3]) newMode = i;
}
This segment of code figures out which region of PlayArea
the mouse is on. TRANSLATE,
IN_A, and IN_B
are public static final variables
or constants; they are used to make values of the mode easy to
identify. The only purpose is to compute the newMode.
...
if (mode == TRANSLATE) {
if (mode != newMode)
{
mode
= newMode;
start_roll
= true;
}
The TRANSLATE mode is the
easiest mode, because the shape already follows the mouse's pointer.
The only thing it must do is prepare the shape for a roll if the
newMode is different from
TRANSLATE. The Boolean variable,
start_roll, is used as a
flag to run some code later. Actually, a better way to handle
starting a roll is to construct a method called startRoll.
The code that computes the newMode
probably should be its own method, too.
}else
{
/* .. mode = IN_A
or IN_B .. */
if (mode != newMode)
{
/*
.. not in roll region anymore .. */
mode
= TRANSLATE;
if
(ang < sign(angle)*angle/2) {
group.pos
= oldPos;
if
(ang>SMALL_ANG) sounds[THUD].play();
}else
{
sounds[THUD].play();
rotations++;
changedThings();
}
solid.orient
= group.element[group.pos];
This section deals with being on a rough surface. If the newMode
is different from the old mode (this means that the mouse is off
the rough surface it was on), you must ensure that the shape is
in one of the standard positions and not partially rotated. This
is one of the underlying properties of the modes; a given mode
assumes that the shape has a certain orientation. If the shape
is not in one of the fixed positions, you take it to the closest
fixed position. This is determined by how close ang
is to angle, where ang
is the current angle of rotation of the shape relative to the
last fixed position, and angle
is the amount of rotation needed to get from the last fixed position
to the new fixed position. So the question really is, Is ang
closer to zero or to angle?
If it falls back to the old position, it will only make a THUD
noise if the fall was big enough-that is, ang
exceeded SMALL_ANG. Otherwise,
the shape falls to the new position, the number of rotations
is incremented, and CopyCat
is informed of the change.
} else {
/*
.. in roll region .. */
ang
= (vec[mode][0]*(x-lastPt[0]) + vec[mode][1]*(y-lastPt[1]))*
ROLL_VALUE/solid.W;
if
(ang<0) {
ang
= 0;
lastPt[0]
= x;
lastPt[1]
= y;
}
if
(ang>Math.abs(angle)) { /* .. finished one rotation .. */
rotations++;
changedThings();
sounds[THUD].play();
solid.orient
= group.element[group.pos];
start_roll
= true;
}
else {
tmp.Rotation(1,2,sign(angle)*ang);
solid.orient
= mid.Times(tmp.Times
Â(mid.Transpose().Times(group.element[oldPos])));
}
}
}
Here, the mode has not changed; the shape is still on a rough
surface. The variable ang
represents the angle at which the shape will be rotated toward
the new position. It depends on how far away the mouse position
is from the start of the roll, lastPt,
in a certain direction. The interface works like a ratchet; when
putting in a bolt, the ratchet is free to turn counterclockwise
and the bolt is not turned, but the ratchet locks into the last
position it passed when it is turned clockwise, and then the bolt
can be turned. Taking this analogy back to the interface, the
shape rolls freely when you move the mouse in one direction, analogous
to the counterclockwise ratchet. Moving the mouse in the other
direction locks the rolling mechanism of the shape, like the locked
clockwise-turning ratchet. Here, if ang
turns out to be negative, the shape does not rotate; with a positive
ang, however, the shape does
rotate. After ang reaches
angle (the angle of the next
position), you must increment the number of rotations,
inform CopyCat of the change,
make the THUD noise, and
start a new roll. Otherwise, you must calculate the orientation
of the shape in the intermediate position between the old fixed
position and the new fixed position.
if (start_roll){
oldPos = group.pos;
group.pos = group.arrows[group.pos][mode];
angle = mid.FindTran(solid.orient,group.element[group.pos]);
lastPt[0] = x;
lastPt[1] = y;
start_roll = false;
}
Here, you finally have the code for how to start a new roll. The
old fixed position, oldPos,
is saved and the new position, group.pos,
is figured out. The angle
to the new position is calculated, and the matrix mid
is computed to help figure out the intermediate positions. Finally,
the lastPt is recorded so
that it can be used for the calculation of ang.
repaint();
return true;
}
return false;
}
All the changes then are thrown to the screen with repaint.
return true; tells the event
handler that the mouseDrag
has been handled. It turns out that the only real things you affected
in this whole method are the orientation and position of the shape.
It is a good idea to keep the event handlers as simple as possible,
because these method are called very often. Even though the code
looks long, because there are really only three methods in this
method, it is fairly fast. Figures 18.3 and 18.4 demonstrate two
drags.
Figure 18.3 : A drag in the TRANSLATE mode.
Figure 18.4 : A drag in the IN_B mode.
mouseUp
The mouseUp method associates
releasing the mouse button with letting the shape go. If the shape
is let go close enough to a place where it fits on the Board,
the top face of the shape is placed on the Board.
Otherwise, the shape is placed by changing the height
variable. One other case also must be handled; if the player drops
the shape outside of PlayArea,
it brings the shape back to the initial position. Of course, not
taking care of that case would mean that the player would never
be able to retrieve the shape, which would really slow down the
game.
/* - - - - - - - - - - - - - - - - - - - &nb
sp;- - - */
public boolean mouseUp(java.awt.Event evt, int x,
int y) {
/* .. place the pattern down .. */
if (onSolid && !animation_running){
/* .. See if it's close enough
to a location that is the same type .. */
onSolid = false;
height = -0.05;
if (Board.ClosestFace(x,y,CLOSE_ENOUGH))
if (group.blank[Board.face[Board.closestface]]==group.blank[group.pos]){
solid.cenx
= Board.vertex[Board.closestface][0];
solid.ceny
= Board.vertex[Board.closestface][1];
Board.faceTry[Board.closestface]
= group.smallestFace[group.pos];
Board.finished
= (Board.nvert == Board.NumCorrect());
changedThings();
if
(level1) solid.ExplodePrep(group.tilt);
else
solid.StampPrep(group.tilt);
new
Thread(this).start();
}
if (x<0 || x>W || y<0
|| y>H) {
goToStart(false);
repaint();
}
return true;
}
return false;
}
If the shape is placed on the Board,
there is some bookkeeping to take care of: centering the shape
over the Board where the
top face will be placed, updating the Board
and notifying CopyCat of
the change, and starting the animation of placing the top face
on the player's copy of the picture. The animations are either
an explosion (level 1), where the top face lands on the Board
(this takes about 10 frames); or a 20-frame animation (level 2),
where the shape flips and stamps the Board,
leaving a mirror image imprint of the top face. See Figures
18.5 and 18.6.
Figure 18.5 : Dropping the shape in a location not desired.
Figure 18.6 : Dropping the shape in a place it fits on the board--the top face drops to the board and the other faces blow away.
Computer games take on many different forms, so it is difficult
to come up with useful programming tips and examples that would
apply to all games. I have two principles that I follow in game
design. One is to delegate the responsibilities of managing the
game world to simpler objects that communicate with one other.
Another is to search for a responsive, intuitive interface-some
natural way that players can communicate their will to the game's
world. With these ideas for designing an effective interface and
an efficient internal representation of the game world, a good
game concept can be turned into a good game. Without these designing
ideas, however, this same concept easily could turn into one of
the many "cr-applets" on the net.
I have illustrated these ideas with the game CopyCat.
I broke down CopyCat into
its functional parts, which communicate with each other to present
and manage the game's world. In general, decomposing a game into
usable objects can be very tricky. Deciding where to draw the
lines between objects depends on not only the specific original
intended uses of the objects, but also on possible future uses.
Developing an eye for pattern recognition, along with learning
good data structures, will help you develop this skill of decomposition.
For beginning game programmers to learn this skill, it is good
practice to constantly revise and redesign the internal workings
of games to try to push the flexibility of their games. The flexibility
of a game is a good measure of how well the internal workings
of the game were thought out.
Also, CopyCat has a responsive
intuitive interface. The essence of CopyCat's
interface is that for every action the player makes in the game,
there is a response so that the player knows the computer understood
the attempted action. Every time the player "picks up the
shape," for example, it raises slightly; the shape lowers
when the player lets go of it. The control of the rolling allows
for many intermediate positions. Generically, this is like a conversation
between the player and the game. The player says, "blah blah
blah," and the game has some response. The game may merely
nod so that the player can continue talking, knowing that the
last statement was understood; or, the game may do something more
noticeable, depending on what the player has said. In designing
the interface, the programmer is designing a limited language
that the player uses to interact or converse with the game. The
simpler or more familiar the language, the easier it is for the
player to play the game. In CopyCat,
the interface is relatively easy because it draws on people's
experiences with the physical world: rolling and sliding shapes,
ratchets, ink stamps, puzzles, and probably a lot more. This physicality,
along with the drag-and-drop idea, makes the interface/language
familiar to a lot of people.
With these principles, a sprinkle of the ideas from Chapter 12,
"Network Programming with Java," some laser pistols
or swords, and a lot of sleepless nights, you could be the creator
of the greatest net game to ever be caught in the Web.
The Web has changed not only the way we view information, but
also the way we interact with it. Web games can help us acquire
the new life-skills of this information age.
The Java language provides simple but powerful tools for designing
professional interfaces. The CopyCat
applet shows the ease of dealing with the mouse events to communicate
complex messages to the world of the game. Now it is up to you
to tame the event handlers and catch some sockets and do some
serious playing around.
Next
Previous
Contents
|