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

private void mnuEdit_Click(object sender, System.EventArgs e) { Editor theEditor = new Editor();

theEditor.Show();

theEditor.OpenGame(theDungeon);

this.Dispose(); } // end game_load

The Edit menu also closes the game form, but first, it creates an instance of the Editor class, opens the current dungeon in the editor, and displays the Editor class on the screen with its Show() method.

Writing the Editor Class

The adventure program would have been interesting if I had stopped at the Game class. I think that adding the editor makes the game much more interesting, though, because it enables the user community to create many adventures. The Editor class is more challenging than the Game class, but it isn’t too tricky.

Creating the Editor Form's Visual Design

Visually, the editor form looks much like the game form but is designed to let the user edit each field. Figure 9.21 shows the editor form's visual layout. The central description for each room is a text box instead of a label, and each direction is represented with a drop−down list box populated with the names of all the rooms in the dungeon. The user navigates through the rooms with Next and Prev buttons. The editor features save and load dialogs, and its menu structure is slightly more complex than the game program because it allows for saving a game, as well as creating a new game from scratch, closing the editor, and playing the current game.

Figure 9.21: The layout for the editor is similar to the game form, but the controls can be edited.

269

Building the Editor Class Instance Variables

The instance variables for the Editor class are much like those for the Game class. theDungeon is used to store all the game data, and roomNum stores the index of the current room:

private Dungeon theDungeon = new Dungeon(); private int roomNum = 1;

Initializing in Editor_Load()

As in the game program, I chose to do my own initialization in the Load() event. The setupRooms() method initializes all the rooms to a default value, and setupCombo() assigns the combo boxes the names of the rooms in the dungeon.

private void Editor_Load(object sender, System.EventArgs e) {

setupRooms();

setupCombos();

} // end editorLoad

Setting Up the Rooms

The setupRooms() method is used to initialize the rooms. It is called when the class first loads and when the user calls for a new game:

private void setupRooms(){ //initialize rooms

int i;

for (i = 0; i < theDungeon.NumRooms; i++){ theDungeon.Rooms[i] = new Room(

"room " + Convert.ToString(i), "", 0,0,0,0);

} // end for loop

}// end setupRooms

The method uses a for loop to step through each room in the dungeon and set its values to appropriate default values. I chose to have the room names include a string representation of the room number because I think that it makes editing a game much easier.

Setting Up the Combo Boxes

The user will edit the game by creating a room at a time. In each room, by using a series of combo boxes, the user will determine what happens when the player goes in a particular direction. The combo boxes contain the current list of room names. Each time the user changes a room name, all the combo boxes need to be updated:

private void setupCombos(){

//ensures the combo boxes are up−to−date int i;

//clear the combos cboNorth.Items.Clear(); cboEast.Items.Clear(); cboSouth.Items.Clear(); cboWest.Items.Clear();

270

//repopulate the combos

for (i = 0; i < theDungeon.NumRooms; i++){ cboNorth.Items.Add(theDungeon.Rooms[i].Name); cboEast.Items.Add(theDungeon.Rooms[i].Name); cboSouth.Items.Add(theDungeon.Rooms[i].Name); cboWest.Items.Add(theDungeon.Rooms[i].Name);

} // end for loop

//preselect room zero cboNorth.SelectedIndex = 0; cboEast.SelectedIndex = 0; cboSouth.SelectedIndex = 0; cboWest.SelectedIndex = 0;

} //end setupCombos

The easiest way to update the combos is to clear them out completely and rebuild them. The Items property of the combo box has a Clear() method, which performs this task admirably. The method then steps through each room, adding each room’s name to each combo box. Finally, the method presets each combo so that it points to room 0.

Trick As I developed examples for this chapter, I started evolving my own convention about the games developed with this kit. I reserved room 0 as the “You can’t go there” room and used room 1 as the basic startup room. For that reason, when you call up an adventure in the editor, it will begin in room 0, but if you load the same file into the Game interface, it will begin in room 1.

Showing a Room

The user will be able to move between the rooms with the command buttons at the bottom of the screen. It is important to be able to display any given room:

private void showRoom(){ //displays a room in editor

setupCombos();

txtName.Text = theDungeon.Rooms[roomNum].Name; txtDescription.Text =

theDungeon.Rooms[roomNum].Description;

cboNorth.SelectedIndex = theDungeon.Rooms[roomNum].North; cboEast.SelectedIndex = theDungeon.Rooms[roomNum].East; cboSouth.SelectedIndex = theDungeon.Rooms[roomNum].South; cboWest.SelectedIndex = theDungeon.Rooms[roomNum].West; lblRoomNum.Text = "room " + Convert.ToString(roomNum);

} // end showRoom

The first task is to reset the combo boxes to take into account any changes in the room data. After that, the method copies the name and description to the appropriate text boxes. Rather than copy the direction values into the database, these numeric values are used to set the index of the combo boxes to the appropriate value, which will display the room number associated with the room. For example, if the North value of the current room is 3, the third element of the combo box will be the name of room 3, because of the setupCombos() call. Setting cboNorth.SelectedIndex to 3 causes the third element of the combo box to appear, which will be the name of room 3.

271

Storing a Room

Storing a room is the logical opposite of saving the room. Basically, the method copies values from the form elements to the current room. Note that the selected index of the direction combos is set, not the text value:

private void storeRoom(){

//stores the current room to the database theDungeon.Rooms[roomNum].Name = txtName.Text; theDungeon.Rooms[roomNum].Description =

txtDescription.Text;

theDungeon.Rooms[roomNum].North = cboNorth.SelectedIndex; theDungeon.Rooms[roomNum].East = cboEast.SelectedIndex; theDungeon.Rooms[roomNum].South = cboSouth.SelectedIndex; theDungeon.Rooms[roomNum].West = cboWest.SelectedIndex;

} // end storeRoom

Trick The relationship between the directional values and the list boxes illustrates an important point. The numeric values are convenient from the programmer’s perspective because they are unambiguous. It is very easy to see which room number to display next if the user clicks the North label. However, human users much prefer text or visual cues to numeric values. The value of the combo boxes is how the way to bridge this gap. When I’m interested in the actual numeric value associated with a direction, I use the selectedIndex property. The user can just deal with the string values without knowing or caring that the position of something in the list box is what matters to the program, not what it says.

Responding to the Next and Prev Button Events

The Next and Prev buttons are used to let the user navigate between records in the game editor. They do a lot of work, but most of that work is encapsulated into the storeRoom() and showRoom() methods you’ve already seen:

private void btnPrev_Click(object sender, System.EventArgs e) {

storeRoom();

roomNum−−;

if (roomNum < 0){ roomNum = 0;

} // end if showRoom();

} // end btn prev

private void btnNext_Click(object sender, System.EventArgs e) {

storeRoom();

roomNum++;

if (roomNum >= theDungeon.NumRooms){ roomNum = theDungeon.NumRooms − 1;

} // end if showRoom();

} // end btnNext click

The btnPrev_Click event stores the current room to preserve any changes that have happened. It then decrements the room number and checks whether the room number is less than 0. If so, the room number is set to 0. The call to showRoom() shows the room, based on the current room number.

272

The btnNext_Click event works in very much the same way, except that it increments the variable, rather than decrements it, and checks whether the value is larger than or equal to the number of rooms in the dungeon. If so, roomNum is set to the number of rooms—1. (Remember, arrays begin with an index of 0, so the largest possible value will be theDungeon.NumRooms − 1.

Saving the Adventure

The adventure game is saved with the now familiar binary serialization technique. The method starts by pulling the game’s name from its textbox and assigning the result to the Name property of theDungeon. Then the current room is stored in case changes have been made but the Next or Prev button hasn’t been clicked. The data is stored in the file, using a FileStream and a BinaryFormatter:

private void mnuSaveAs_Click(object sender, System.EventArgs e) {

//get the game's name theDungeon.Name = txtGameName.Text; //store the current room storeRoom();

//write the data out to a binary file FileStream s;

BinaryFormatter bf = new BinaryFormatter();

if (fileSaver.ShowDialog() != DialogResult.Cancel){

s = new FileStream(fileSaver.FileName, FileMode.Create); bf.Serialize(s, theDungeon);

s.Close();

} // end if

}// end mnuSave

Loading the Adventure

Loading the adventure works just as it did in the Game class, using binary serialization. After I loaded the game in memory, I set the room number to 1 and showed the room:

private void mnuOpen_Click(object sender, System.EventArgs e) {

//read the data from a binary file FileStream s;

BinaryFormatter bf = new BinaryFormatter();

if (fileOpener.ShowDialog() != DialogResult.Cancel){

s = new FileStream(fileOpener.FileName, FileMode.Open); theDungeon = (Dungeon) bf.Deserialize(s);

roomNum = 0; showRoom(); s.Close();

} // end if

}// end mnuOpen

Opening a Game

As in the Game class, it will be possible for the MainForm to start the editor remotely, so I added an OpenGame() method that will start the editor when given a Dungeon as a parameter:

public void OpenGame(Dungeon passedDungeon){ theDungeon = passedDungeon;

roomNum = 0;

txtGameName.Text = theDungeon.Name; showRoom();

273

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