
Description
This is a simple game written in C# in which the user moves a packman like player around the form and gobbles up red dots. The object is to get all the dots in as quick a time as you can. The design for the game is shown below:

Fig 2 - The UML for this game was reverse engineered using WithClass 2000
By examining the design, you can see that the eater game is not so difficult to understand because its simply a group of classes that are aggregates of the Form.
Each instance of class draws itself on the Form and the methods of the class are called from the form to exercise the class's behavior.
The Sequence of Events is shown in the WithClass UML Diagram below after an arrow key to the right is pressed:

Fig 3 - Sequence of Events after a Right Key is pressed and the Eater hits a stone
The diagram above shows the flow of methods through the objects when a right arrow key is pressed and for those of you new to UML its known as a sequence diagram. The messages on the arrows map back to methods in the classes and each box with a vertical line represents an instance of the class in our Eater Game.
The code for the sequence above is shown below:
Listing 1 - KeyDown Event Handler
// KeyDown Event handled by the Formprivate void Form1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{ // Invalidate the Eater before moving it string result = e.KeyData.ToString();
Invalidate(TheEater.GetFrame());switch (result)
{case "Left":// Move the Eater to the LeftTheEater.MoveLeft(ClientRectangle);
Invalidate(TheEater.GetFrame());break;case "Right":// Move the Eater to the RightTheEater.MoveRight(ClientRectangle);
Invalidate(TheEater.GetFrame());break;case "Up":// Move the Eater to the UpTheEater.MoveUp(ClientRectangle);
Invalidate(TheEater.GetFrame());break;case "Down":// Move the Eater to the DownTheEater.MoveDown(ClientRectangle);
Invalidate(TheEater.GetFrame());break;default:break;
} // Check to see if the Eater ate a stoneint hit = CheckIntersection();if (hit != -1)
{// The Eater Ate a Stone, Increment the score, play a sound and remove the stoneTheScore.Increment();
PlaySoundInThread("hit.wav");
Invalidate(TheScore.GetFrame());
Invalidate(((Stone)Stones[hit]).GetFrame());
Stones.RemoveAt(hit);// Check to see if the game is overif (Stones.Count == 0)
{// Game is over , all the stones are eaten, show the time it took to eat themMessageBox.Show("You Win!\nYour time is " + TheTime.TheString + " seconds.");
Application.Exit();
}
}
In the Key Handler code above, each case of movement is handled for the Eater player. If the Eater finds a stone, the score is incremented and a stone is removed.
Drawing of the stones, the eater, the score, and the timer is all handled by the each of the respective classes. Below is the OnPaint method for the form to draw the board:
Listing 2 - Paining the Game Board
private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
Graphics g = e.Graphics;
g.FillRectangle(Brushes.White, 0, 0, this.ClientRectangle.Width,
ClientRectangle.Height); // draw the scoreTheScore.Draw(g);// draw the timeTheTime.Draw(g, TheSeconds);// draw the stones for (int i = 0; i < Stones.Count; i++)
{
((Stone)Stones[i]).Draw(g);
} // also draw the eater TheEater.Draw(g);
}
Animation of the Eater is handled by using two bitmaps. One bitmap has the eater with its mouth open, the other with its mouth closed. The Eater is drawn on the odd pixels with the mouth open and the even pixels with the mouth closed. It also checks to see if it moved horizontally the last time or vertically. This could probably be expanded to 8 images, two for each direction the eater moves in.
Listing 3 - Drawing the Eater
public void Draw(Graphics g)
{
Rectangle destR = new Rectangle(Position.X, Position.Y, EaterImage.Width,
EaterImage.Height);
Rectangle srcR = new Rectangle(0,0, EaterImage.Width, EaterImage.Height);// make it look like the mouth is movingif ( ((Position.X % 2 == 1) && ((Position.X - LastPositionX) != 0)) ||
((Position.Y % 2 == 1) && ((Position.Y - LastPositionY) != 0))
)
g.DrawImage(EaterImage, destR, srcR, GraphicsUnit.Pixel);elseg.DrawImage(EaterImage2, destR, srcR, GraphicsUnit.Pixel);
LastPositionX = Position.X;
LastPositionY = Position.Y;
}
Invalidation
Back in the days when people were programming Macintoshes (remember the toolbox!), even before Windows 3.1 came into existence there was the concept of Invalidating regions of the window. I must admit its not an intuitive concept, but seems to work well. The concept goes that when erasing and redrawing an area of the window, you don't simply erase and redraw the area. You "Invalidate" the part of the window you want redrawn. This minimizes flicker in that area. The actual drawing is done in the OnPaint Routine (Or Form1_Paint in this example). Anotherwords you invalidate a rectangular region you want to redraw, this forces the Form1_Paint to be called and go through the entire Form1_Paint routine. Everything in Form1_Paint is executed, but only the area of the screen that you Invalidated is actually drawn. In the Listing 1 Key Handler, the Eater area is first invalidated to erase the original position of the Eater and then the new area of where the eater is being drawn to after the move is invalidated. The effect to the eye is that the eater is actually moving. The eater can be made to "speed up" by increasing the increment at which the Position is changed when the key is being pressed.
Improvements
I was actually thinking of a few things that could be introduced to the game to give it more of a twist. The game can be changed to have the timer time down and the score could be based on the number of stones eaten in that time. A maze object could be introduced on the board to make it a little more challenging to get the stones. Maybe a bullet object could be created to allow the eater to shoot at the stones. Perhaps you'll see Eater II or Super Eater in the near future ;-)
0 comments:
Post a Comment