Reader Level:
ARTICLE

Daleks in .NET

Posted by Mike Gold Articles | Games Programming C# December 11, 2002
The other night my girlfriend dragged me down to the NYC Public Library claiming that they had a huge Science Fiction movie section. Admittedly my taste in movies and my wife's taste in movies can vary slightly, however, I was willing to take a chance, given that rentals at the library are free as opposed to $4 at the local video store.
  • 0
  • 0
  • 19670
Download Files:
 



Figure 1 - Dalek from the Dr. Who Series

The other night my girlfriend dragged me down to the NYC Public Library claiming that they had a huge Science Fiction movie section. Admittedly my taste in movies and my wife's taste in movies can vary slightly, however, I was willing to take a chance, given that rentals at the library are free as opposed to $4 at the local video store. Of course I had visions of "The Matrix" and "Terminator II",  however,  when I arrived at the Science Fiction Movie Section, I was surprised to discover that it contained shelves and shelves of the Dr. Who series on one side and Star Trek on the other. The last time I had watched Dr. Who was about 5 Doctors ago, but I admit  I was intrigued with the possibility of reawakening my memories of the good doctor.  Luckily, there was a Doctor Who fan in the library isle as I looked somewhat perplexed at the 100 Dr. Who episodes aligned on the shelves. He recommended the "Five Doctors Episode". I also noticed hiding between the videos was a 2 part episode of The Daleks, a somewhat classic race of killer robots that scream incessantly, "You will be exterminated". 

This brought to memory a game I used to play on the Macintosh called Daleks, modeled after the Daleks in the Dr. Who series. The object of the game was to run around a grid avoiding Daleks and cause them to exterminate each other until they were all dead.  Daleks in the game behave somewhat like homing pigeons, always taking the shortest route to your demise. The trick was to get 2 or more Daleks to collide with each other on route to your player.  After the Daleks collided, they formed a sort of death-pile inside the square they collided. The player could take advantage of this death-pile to lure other honing Daleks into it. Along with moving your character around one square at a time, your player was endowed with a few special powers. The player had the ability to teleport randomly on the grid (risking the chance of teleporting directly into a Dalek). Also the player is equipped with a limited number of sonic blasts in which they could rid themselves of adjacent Daleks.

Inspired by Dr. Who and my memories of the Mac, I've recreated the game in .NET.  Below is a partial screen shot of the game where Dr. Who is fleeing along the grid from the deadly Daleks:



Figure 2 - Dalek Game Screen Shot

The rules of the game are simple. Run away from the daleks and try to get them to collide with each other as you are avoiding them. This will form those "death-piles" we talked about in which you can lure the unsuspecting daleks who are only focused on your current position:



Figure 3 - Two Daleks moving to their demise towards a death-pile

Game control is fairly straightforward. Below is the description of each control. You can use the numeric keypad with the Num Lock off to perform all of the functions:

Key Function
Left Arrow  Move Player Left
Right Arrow Move Player Right
Up Arrow Move Player Up
Down Arrow Move Player Down 
Home Move Player Up and to the left
End Move Player Down and to the Left
Pageup Move Player Up and to the Right
PageDown Move Player Down and to the Right
S  or Insert  Sonic Blast (Shield)
T or Delete Random Teleport

Table 1 - Table of Player Keys

The design of Daleks utilizes many of the classes from other games I developed on this site such as Space Invaders or Eater Game II. Below is the UML class design of the Daleks game reverse engineered with WithClass:



Figure 4 - UML Design of Daleks Game Reverse engineered with the WithClass UML Tool for C#

In this design, the GameManager class hosts most of the functionality for controlling the game. The Form1 class merely responds to events such as painting and keypresses and passes the events on to the GameManager. The GameManager plays sounds, draws the figures, and handles the game control logic for  moving Daleks, moving the Doctor, keeping score, and tracking collisions. The GameManager uses the Dalek class and the Who class simply to draw the game pieces.

There are many aspects of the game that we can talk about in this article. The one we will focus on here, is how to animate the sonic blast using a state engine.  Below is our Dr. Who player performing a sonic blast on an unwary Dalek:



Figure 5 - Sonic Blast from Dr. Who

The sonic blast sends concentrics rings eminating out to adjacent squares killing the Daleks occupying them. The concentric wave appears to expand out to the squares and then collapse back into itself. This effect is accomplished by drawing a certain number of concentric circles around the doctor each time the timer event handler is entered in the form.  The event handler of a timer tick being triggered is shown below:

Listing 1 - Timer Event Handler for Sonic Shield Animation

private
void timer1_Tick(object sender, System.EventArgs e)
{
// if the shield is activated, trigger the next shield state in the GameManager
if (Manager.ShieldOn)
{
Manager.NextShieldState();
}
}

The GameManager uses a simple State Engine described by the state diagram below in Figure 6:



Figure 6 - State Diagram of Animated Sonic Blast drawn with WithClass

On each tick of the timer, the next state is entered and the number of rings is increased or decreased according to the state. On the last state, the end state, the shields are turned off and the adjacent daleks are killed.  The state machine is implemented using an enumeration for each state and a switch statement for control logic through the state.  As you can see, the switch statement follows the diagram exactly.  We move through the states by calling NextShieldState on each timer tick. Transitions are handled by assigning CurrentShieldState to the next state for transition, for example, CurrentShieldState=ShieldState.third. Actions are taken inside each case of the switch statement.  In most cases, the action is just assigning the number of concentric rings to be painted later by the form.

Listing 2 - State Engine Method in the GameManager for animating a sonic blast

enum ShieldState {idle, first, second, third, collapse3, collapse2, collapse1, end}; // various states
ShieldState CurrentShieldState = ShieldState.idle; // set the initial state to idle
int NumShieldRings = 0; // Declare shield ring count
// ************ State Engine for Animating the Sonic Blast *****************
public void NextShieldState()
{
switch (CurrentShieldState)
{
case ShieldState.idle:
NumShieldRings = 1;
CurrentShieldState = ShieldState.first;
break;
case ShieldState.first:
NumShieldRings = 2;
CurrentShieldState = ShieldState.second;
break;
case ShieldState.second:
NumShieldRings = 3;
CurrentShieldState = ShieldState.third;
break;
case ShieldState.third:
NumShieldRings = 4;
CurrentShieldState = ShieldState.collapse3;
break;
case ShieldState.collapse3:
NumShieldRings = 3;
CurrentShieldState = ShieldState.collapse2;
break;
case ShieldState.collapse2:
NumShieldRings = 2;
CurrentShieldState = ShieldState.collapse1;
break;
case ShieldState.collapse1:
NumShieldRings = 1;
CurrentShieldState = ShieldState.end;
break;
case ShieldState.end:
NumShieldRings = 0;
CurrentShieldState = ShieldState.idle;
ShieldOn =
false;
KillAdjacentDaleks();
// all daleks next to shield die
MoveDaleks();
DetermineScore();
// see if doctor died, and if so draw him dead
HandleDoctorDeath();
// test to see if all Daleks have died
HandleNewGame();
break;
default:
break;
}
// invalidate the doctor square so the sonic blast will redraw
Rectangle r = Doctor.GetFrame();
r.Inflate(r.Height, r.Width);
TheForm.Invalidate(r);
}

The sonic shield is drawn each time in the paint routine of the form which calls the GameManager's Draw method which in turn calls the DrawShield method. Therefore DrawShield will be automatically triggered on each Invalidate call of the NextShieldState method. The Draw method of the GameManager calls DrawShield to draw the current state of the shield. DrawShield draws the concentric rings using the DrawEllipse function in the Graphics class. 

public
void DrawShield(Graphics g)
{
// find the mid point of the doctor
int x = Doctor.X * Grid.kSquareWidth + Grid.kSquareWidth/2;
int y = Doctor.Y * Grid.kSquareWidth + Grid.kSquareWidth/2;
// loop through the number of rings and draw larger and larger circles encompassing the doctor
for (int i = 0; i < NumShieldRings; i++)
{
g.DrawEllipse(Pens.Black, x - i*12, y - i*12, i*24, i*24);
}
}

The effect of drawing an increasing number and then decreasing number of concentric circles on each invalidation of the Doctor's square gives us our sonic blast animation.

Conclusion

Daleks is fun to play and will challenge you at the higher levels. Each time you defeat a board, the next board has an increased number of Daleks. The high score is tracked by the program, so you can test your "Dalek Extermination" prowess. If you want to travel back into the world of  the Doctor and the Daleks, check out some of the references below or head down to the NYC Public Library on 42nd Street and 5th Avenu.

References

Daleks Wave Archive
Digital Wave Sound File Archive
Doctor Who - The Official Site

The Doctor Who Home Page

COMMENT USING

Trending up