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

pixels, they looked good. It’s smart to get and learn a good graphics package (such as The Gimp, included on the CD−ROM that accompanies this book) so that you can create your own images without the headache of dealing with intellectual property rights.

Getting a good starting position for the images was tricky. Although the position of the images doesn’t matter at all in terms of game play (at least, the way the game is set up right now), it is important that the players at least appear to be dispersed around the field. I decided to place all the players at the center of the field horizontally. If you subtract the player’s width from the field width and then divide by 2, you get the appropriate placement to center the player horizontally. Getting a good vertical position took more thought because the players should start out in different parts of the field. The center should be close to the opponent’s goal, and the fullback should be near the user’s goal. I found that if I divided the field height by the player number, I came up with a good vertical placement for each player except the goalie. I excluded the goalie from this calculation with an if statement because it will be placed more carefully in a later method. Also, excluding the goalie is necessary because its player number is 0 and the computer will choke if you ask it to divide by 0.

Later on, I’ll need to know which player is which, so I used a very handy property named the Tag property. The Tag is basically a free−for−all property you can use to store any kind of information you want. I’ll store the player number in the tag property for use later on in the click event.

All the picPlayer controls must respond to a click event, so I registered an event handler to a method named clickPlayer (which I’ll build shortly).

Finally, I added each picture box to the field panel.

Setting Up the Opponents

The opponents are much like the player array, but because they won’t respond to events, they are simpler to set up:

private void setupOpp(){ //set up opponents

int x; int y;

for (int i= 1; i < 5; i++){ picOpp[i] = new PictureBox(); picOpp[i].Size = new Size(25,25);

x = (int)((this.Width − picOpp[i].Width)/2); y = (int)(pnlField.Height/i − 30); picOpp[i].Location = new Point(x, y); picOpp[i].Image = myPics.Images[0]; picOpp[i].SizeMode =

PictureBoxSizeMode.StretchImage;

pnlField.Controls.Add(picOpp[i]);

} // end for loop

}// end setupOpp

The size and position of each picture box require a formula very much like the one in setupPlayer(). I set each image to the red player without a ball, which is image number 0 in the image list. I added each picture box to the controls of the panel, but adding an event listener was unnecessary.

227

Setting Up the Goalies

Both the goalies are part of the picPlayer array, and both have already been set up, for the most part. However, they deserve special treatment because they are positioned differently than the other players and have different images. Note that the user’s goalie is named GOALIE, and the opposing goalie is named SHOT.

private void setupGoalies(){ //place goalies more carefully int x;

int y;

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

y = pnlField.Height − picPlayer[GOALIE].Height− 20; picPlayer[GOALIE].Location = new Point(x, y); picPlayer[SHOT].Location = new Point(x, 0); picPlayer[GOALIE].Image = myPics.Images[5]; picPlayer[SHOT].Image = myPics.Images[4];

} // end setupGoalies

I used the now familiar horizontal centering routine to get the horizontal placement of the goalies. I moved the user goalie right above the bottom of the field and placed the opposing goalie near the top of the field. I then assigned appropriate images to the goalies from the image list.

Responding to Player Clicks

The game’s action comes in two places. The passage of time will move the various player images on the field, but this has no real bearing on the game. The most important actions come when the user clicks one of the player images. Each of these images was registered with the clickPlayer() method as its event handler. This method takes action based on the algorithm described in the Shot Demo program earlier in this chapter:

private void clickPlayer(Object sender, EventArgs e){

//happens whenever you click a player Random roller = new Random();

double toSucceed; double myRoll;

//figure out which player was clicked PictureBox thePic = (PictureBox)sender;

int playerNumber = Convert.ToInt32(thePic.Tag); nextPlayer = playerNumber;

//figure out how likely success is toSucceed = shotChance[currentPlayer,

nextPlayer];

//roll a random double myRoll = roller.NextDouble();

//Announce what's going on string message = "From: " +

playerName[currentPlayer]; message += " to: " +

playerName[nextPlayer] + " "; message += toSucceed.ToString(); lblAnnounce.Text = message;

228

//look for success

if (myRoll < toSucceed){ goodShot();

}else { badShot();

}// end 'pass succceeds' if

}// end clickPlayer

This method uses a number of variables to get started. It will need to know which player currently has the ball and to which player the user intends to pass. I’ll need to do a little technical gymnastics to squeeze out this information, but it is accessible. The currentPlayer variable was declared at the class level, and its value is preset to whichever player currently has the ball. The trickier part is to determine nextPlayer, because the user doesn’t directly enter this information. Instead, he clicks a picture box. The problem is to determine which picture box the user clicked.

Fortunately, C# provides a couple good tricks for figuring out exactly this type of information. First, recall the sender object that is automatically a parameter of all event handlers. This parameter contains an object that represents whatever object triggered the event. In this case, the picture box that the user clicked will be stored in the sender variable. I cast the sender object as a picture box and stored it in the local variable thePic. Back when I created each picture box, I stored its ID in the Tag property. I can extract that property now and convert it to an integer to determine which player was clicked. That value is stored in nextPlayer.

When I know what currentPlayer and nextPlayer are, I can use them to look up the likelihood of success in the shotChance lookup table. The mechanics of this activity are the same as in the Shot Demo program. The lookup table returns a double value representing the likelihood that the given shot will succeed.

I then send a message to the announcer label of the shot being attempted and the likelihood this shot will succeed. Finally, I evaluate the shot with a random number and call a method based on the results. If the shot succeeds, control flows to the goodShot() method. If the shot fails, the badShot() method takes control.

Handling Good Shots

If the shot is successful, the next step depends on what the user was aiming at. A successful shot on the goal means that the user has scored. If the user was aiming anywhere else, the ball should be passed to that player.

public void goodShot(){ //if the shot succeeded

//check to see if it's a shot on goal if (nextPlayer == SHOT){

playerScore++;

updateScore();

MessageBox.Show("Goooooaaaaalllll!!!!");

setPlayer(HALFBACK);

}else {

lblAnnounce.Text = playerName[nextPlayer] +

"now has ball";

setPlayer(nextPlayer);

} // end 'scored a goal' if

}// end goodShot

229

The if statement checks whether the user is shooting at the goal. If so, nextPlayer is set to the same value as SHOT. The user’s score increases by 1, and the updateScore() method is employed to send a report to the scoreboard. I added a message box to enhance the mood. Finally, I gave the ball to the halfback, using the setPlayer() method.

If the user is not shooting at the goal, another player is now the current player. I copied the value of nextPlayer over to currentPlayer and set the current player visually with the setPlayer() method.

Handling Bad Shots

If the shot did not succeed, the opposing team has the ball, and I’ll give them the chance to score. This is the easiest way to control the difficulty of the game. As the code stands now, the opponent will score about 5 percent of the time the player misses a pass or shot. To make the opposing team better, change the rate to a larger number. To make the opponent weaker, use a smaller number.

public void badShot(){

Random roller = new Random(); double toSucceed;

int playerNumber;

lblAnnounce.Text = "Opponent gets ball..."; //check for opponent goal

toSucceed = roller.NextDouble(); //change the following value to alter

game difficulty

if (toSucceed < .05){ oppScore++; updateScore();

MessageBox.Show("Opponent Scores!!"); setPlayer(HALFBACK);

}else {

playerNumber = roller.Next(5); lblAnnounce.Text += "recovered by " +

playerName[playerNumber];

setPlayer(playerNumber);

} // end opponent scores if

}// end badShot

When the opponent gets the ball, I create a random double and compare it to .05. Five percent of the time, the random value will be smaller than .05, and the opponent will score. If this happens, I increase the opponent’s score, update the scoreboard, and send a message box to the user. I then give the ball to the halfback to restart play.

If the opponent’s shot did not succeed, I randomly determine which player begins with the ball and set that player as the current player.

Setting a New Current Player

Setting a player is a critical part of the game. It happens whenever the ball changes hands (okay, feet). This is a very frequent occurrence in this game. Setting a new player has two main effects. First, I change the variable currentPlayer to the new player’s number, and then I change the visual representation so that the user can see that a new player has the ball:

public void setPlayer(int playerNumber){ //given a player number, shows that player as

230

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