Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Microsoft CSharp Programming For The Absolute Beginner (2002) [eng]-1.pdf
Скачиваний:
46
Добавлен:
16.08.2013
Размер:
15.71 Mб
Скачать

to a list box, but the controls collection is designed to accept controls. The final task for the constructor is to register an action listener to the button. The last line of the method accomplishes this task. myButton.Click refers to the click event of the myButton object. You can use the += operator to add an event handler to this event.

Hint You can add multiple handlers to the event, if you like, so that clicking the button calls several methods. Doing so can cause problems, though, because it is unclear which order the methods would evaluate.

An event handler is a class. To make one, you call its constructor with the name of the method you will use to respond to this event. That event method (like all event methods) will need to have an object and an EventArgs parameter.

Responding to the Button Event

Because I used myButton_Click as a parameter when I built my EventHandler, I have to create a method named myButton_Click:

public void myButton_Click(object sender, System.EventArgs e){

MessageBox.Show("Ouch!"); } // end myButton

The only way this method differs from any other event handler is that the Visual Designer did not create it. I had to write the code by hand, including the parameters. Other than that, the method is no different from many you have written by now.

Building the Soccer Program

You now have all the skills you need to build the soccer game introduced at the beginning of the chapter. The game might appear complex if you look at the entire thing at once, but when you break it into its constituent parts, the soccer game isn’t nearly so intimidating.

Setting Up the Variables

As usual, the variables tell you a lot about the program. The Soccer program features a large number of variables declared at the form level, but they are not tricky.

Variables about the Players

The first group of variables is used to give names to the various positions:

//set up constants for players private int GOALIE = 0; private int FULLBACK = 1; private int HALFBACK = 2; private int WING = 3;

private int CENTER = 4; private int SHOT = 5;

//array for player names private string[] playerName = {

"Goalie",

222

"Fullback",

"Halfback",

"Wing",

"Center",

"Shot"

};

Many times throughout the game code, it is necessary to determine which player you’re talking about. Rather than memorize that the goalie is player 0 and the shot (opposing goal) is player 5, I created special variables to hold these names. The use of all uppercase is traditional when you create a variable like this, which you don’t intend to change throughout the program. You will see how useful these variables are later on as you read through the code.

Likewise, having a string array of strings associated with an array is sometimes handy so that you can easily tell the user what’s going on. The playerName array will typically be used when I want to tell the user something about a player.

Game Management Variables

The next batch of variables is used to control the general flow of the game. These variables are the lifeblood of the game:

//primary game variables private int playerScore = 0; private int oppScore = 0; private int timeLeft = 600;

private int currentPlayer = GOALIE; private int nextPlayer = FULLBACK;

//data structure for likelihood of shot private double[,] shotChance =

{{−1, .8, .6, .4, .1, .01}, {.8, −1, .8, .6, .4, .02}, {.8, .8, −1, .8, .6, .03}, {.8, .8, .8, −1, .8, .04}, {.8, .8, .8, .8, −1, .2}, {.8, .8, .8, .8, .8, −1}};

The first batch of variables in this group holds typical game variables, such as the player and opponent score, time left in the game (in 10ths of a second), which player currently has the ball, and which player is set to receive the ball. Notice the use of GOALIE and FULLBACK. Because I set these variables earlier, I could use them here to make my intentions much clearer than if I’d just put the numbers 0 and 1.

The Picture Box Arrays

The players zipping around on the field are actually picture boxes. These picture boxes are the trickiest part of the game because there are so many of them. To manage them by hand would be crazy, so I built two arrays. You can make an array out of any sort of variable, including picture boxes. Of course, the Visual Designer does not support placing arrays of controls. I’ll have to place them by hand, but it’s not difficult, as you will see:

//custom picture box arrays for players

private PictureBox[] picPlayer = new PictureBox[6]; private PictureBox[] picOpp = new PictureBox[5];

223

Notice that I made two arrays. The picPlayer array deals with the players that the user can click (all the players on the yellow team and the red goalie). The picOpp array contains picture boxes that handle all the players on the red team except the red goalie.

Note that the distinction between the player and opponent arrays is not which team the picture box represents. The red goalie is part of the picPlayer array because it shares a lot of functionality with the players on the yellow team. It can be clicked, and it can be a target to shoot at. None of the other red teams need to respond to click events, and they don’t do anything but move around randomly. The picture boxes that need to respond to events are stored in one array (picPlayer), and those that do not need to respond to events are stored in the other array (picOpp).

Building visual controls with the Form Designer is much easier, so I built everything but the picture box arrays, using the designer:

//controls built by designer

private System.Windows.Forms.Label lblAnnounce; private System.Windows.Forms.Timer timer1; private System.Windows.Forms.Label lblTime; private System.Windows.Forms.Label lblPlScore; private System.Windows.Forms.Label lblOppScore; private System.Windows.Forms.ImageList myPics; private System.Windows.Forms.Panel pnlField;

private System.ComponentModel.IContainer components;

The lblAnnounce label delivers a play−by−play account of the game. It announces each pass and shot, helping the user figure out what’s going on in the game.

The game features a standard timer named lblTime. The timer’s interval is set to 100 milliseconds, which means that the game runs at 10 frames per second.

The lblPlScore and lblOppScore labels are used to display the player and opponent scores, respectively.

The program features an ImageList control named myPics, which contains seven images (even though I didn’t use them all in the final game). The images include a yellow player without the ball, a yellow player with the ball, a red player without the ball, a red player with the ball, a yellow goalie and red goalie, and a large red square I used when debugging. Figure 8.14 shows the various images associated with the image list control.

Figure 8.14: The images stored in the image list control.

224

Hint That large red square turned out to be handy, even though I didn’t include it in the final game. At one point, I had a player just sitting there, not moving. I couldn’t figure out which player it was, so I was unsure how to fix it. One by one, I set the image of each player to the red box and ran the program. After the offending player was set as the red box, I knew which player was having trouble, and I found the problem in the code. Sometimes you have to be inventive in your debugging strategies.

The field itself is a panel, with an image of a soccer field attached. I added all the player picture boxes directly to the panel rather than to the form itself because the panel’s dimensions are a handy way to determine the field's borders. All the other controls (the labels for timing and score) are added to the form itself.

Examining the Constructor

The Soccer program is heavily dependent on custom controls. You might expect a lot going on in the constructor, and you’d be right. However, when you look at the constructor code, you will find it very sparse:

public frmSoccer()

{

//

// Required for Windows Form Designer support

//

InitializeComponent();

setupPlayers();

setupOpp();

setupGoalies();

} // end constructor

There is so much work for the constructor to do that I decided to break it into smaller sections and send off some of the work to methods. (Encapsulation! I love encapsulation!) This makes it very clear what the big picture assignments are in the constructor and enables you to separate the strategic problem (what kinds of things have to happen in the constructor) from the tactical problems (such as how you set up each picture box array). As you can see, the constructor has four major jobs.

The first is to call InitializeComponent(), which manages all the components created in the Visual Designer. Even though I built some controls by hand, calling InitializeComponent() is still important. If you’re going to add other code to the constructor, that code should come after the InitializeComponent() call.

All the other jobs in the constructor involve setting up the picture box arrays. The array of clickable picture boxes has to be set up. The setupPlayers() method will handle this chore. The opponents have to be set up as well. Their setup is a little different, so it will be handled in a different method, named setupOpp(). Finally, the two goalies have slightly different behavior than the other picture boxes, so they need their own setup routine.

Setting Up the Players

The picPlayer array of picture boxes is the user’s main interface with this program. All the user’s interactions with the program will come from clicking one of these players. Because the Visual Designer isn’t good at arrays, you yourself have to set them up. It’s a little tedious, but not difficult.

225

private void setupPlayers(){

//setting up my own picture boxes int x;

int y;

//step through the players for (int i= 0; i < 6; i++){

//standard control setup picPlayer[i] = new PictureBox(); picPlayer[i].Size = new Size(25,25);

//start all players in the center of the field //on the x (horizontal) axis

x = (int)((pnlField.Width − picPlayer[i].Width)/2);

if (i > GOALIE){

//move all players but goalie to a Y value //related to their position (center close to opp), //fullback close to own goal

y = (int)(pnlField.Height/i); picPlayer[i].Location = new Point(x, y);

} // end if

//set up the image to the yellow no ball player picPlayer[i].Image = myPics.Images[2]; picPlayer[i].SizeMode =

PictureBoxSizeMode.StretchImage;

//use the tag field to store the player number picPlayer[i].Tag = i.ToString();

//register the event listener picPlayer[i].Click += new

EventHandler(clickPlayer);

//add the player to the panel pnlField.Controls.Add(picPlayer[i]);

} // end for loop

}// end setupPlayers

A lot is going on in this method, but the comments clearly describe the action. As I’ve noted before, for loops and arrays are a natural combination. In this case, they save quite a bit of work because I can tell the program to step through each player and perform the same work on it, rather than repeat the code six times.

The first major job of the method is to ensure that each member of the picPlayer array is initialized. It’s important to notice that even though I’ve initialized the array, I still need to initialize each of its members by assigning it a picture box.

I set each picture box to be 25 by 25 pixels. This is small enough to hide my poor drawing skills but large enough to be visible, and it causes the players to be scaled properly on the screen. The image of a yellow player without a ball is image number 2 in my image list, so I set each player to that image. I made sure that the picture boxes were set to stretchImage mode so that the images would size correctly.

Trick If you’re not a brilliant artist, all is not lost. The images for many games are actually tiny. I designed my images as 50 x 50 images and then drew them at 400−percent of their original size. My large drawings looked incredibly crude, but when I shrank them down to 25 x 25

226

Соседние файлы в предмете Программирование на C++