Learn VB .NET Through Game Programming [Electronic resources]

Matthew Tagliaferri

نسخه متنی -صفحه : 106/ 68
نمايش فراداده

Chapter 5, “Understanding Polymorphism,” as an example. A good object-oriented developer would probably write a save and load function on each CellularAutomataCell class descendant and then call those methods for each cell within the CellularAutomataGame class. Of course, each descendant of the CellularAutomataCell class has different types of properties to store, so you’d probably have to write methods that could be overridden in each descendant class.

The .NET Framework has made this tedious programming task much easier, you’ll be happy to know. Built into the .NET Framework is the ability to serialize any object into several different predefined formats and then write that formatted data to (and later read from) disk. Better still, you can do this serializing and reading/writing in a few short lines of code.

To demonstrate this functionality, I’ve made a copy of the source code for the CellularAutomata project and added the load/save functionality. You can find this new project in the folder CellularAutomataWithSave.Toimplement the load/save functionality, made the modifications described in the following sections.

Marking Classes As Serializable

Any classes that are going to be sent to disk must first be marked as such. To do this, you add the Serializable attribute to the CellularAutomataCell class and all its descendants. An attribute is a compiler directive that describes some aspect of your class to the .NET Framework. This particular attribute tells the compiler that this class can be serialized to disk. The syntax for the Serializable attribute is as follows:

<Serializable()> _ 
Public Class ConwaysLifeCell 
Inherits CellularAutomataCell 

Note the code continuation character—attributes must reside on the same line as the class definition itself.

You can attach a number of attributes to both classes and class members, and you can create your own attributes to help describe your code in other ways. For example, you can create a special attribute that denotes the author of a class and the date it was last changed. You can then write special programs that actually rip through your compiled programs, extract this custom attribute information, and output it for documentation purposes. Consult the online help for further information on attributes.

Pointing to the Formatters

The next required step is to add Imports clauses to the top of the code module that will be implementing the serialization. In this case, the code module is CellularAutomata.vb:

Imports System.Runtime.Serialization.Formatters.Binary 
Imports System.Runtime.Serialization.Formatters.Soap 

Usually you’d choose to use only one data format and not need both of these references, both formats will be demonstrated here.

Writing the Save Code

The SaveToDisk method you add to the CellularAutomataGame class is laughably simple:

Public Sub SaveToFile(ByVal cFilename As String) 
Dim oFS As New FileStream(cFilename, FileMode.Create) 
Dim oBF As New SoapFormatter 
System.Windows.Forms.Cursor.Current = Cursors.WaitCursor 
Try 
oBF.Serialize(oFS, FCells) 
Catch oEX As Exception 
MsgBox(oEX.Message) 
Finally 
oFS.Close() 
System.Windows.Forms.Cursor.Current = Cursors.Default 
End Try 
End Sub 

All that’s happening here is that a FileStream is opened, a special class named a SoapFormatter is created, and then this class is used to write the data to the stream. The data written in this case is the FCells object, which is an instance of an ArrayList that’s used to store all of the little CellularAutomataCell objects that make up the current game.

Notice that you don’t have to specify how to write out the data. All you’ve done is say “Please output the current state of the FCells object to disk.” The SoapFormatter writes out the ArrayList and all of the objects contained within automatically.

Now, the Simple Object Access Protocol (SOAP) format is an Extensible Markup Language (XML) format used to send .NET classes back and forth across the Internet, and as an XML format, it tends to make rather large files. As an alternative, you can also use a BinaryFormatter class and your on-disk files will be much smaller. To use this class, simply replace the declaration to the new formatter:

Dim oBF As New BinaryFormatter 

Writing the Load Code

The load from disk code is similar to the save to disk code but with the contents of the file loaded into the correct object. In this case, the ArrayList FCells is assigned to the contents of the file:

Public Sub LoadFromFile(ByVal cFilename As String) 
Dim oFS As New FileStream(cFilename, FileMode.Open) 
Dim oBF As New SoapFormatter 
System.Windows.Forms.Cursor.Current = Cursors.WaitCursor 
Try 
FCells = CType(oBF.Deserialize(oFS), ArrayList) 
Catch oEX As Exception 
MsgBox(oEX.Message) 
Finally 
oFS.Close() 
System.Windows.Forms.Cursor.Current = Cursors.Default 
End Try 
End Sub 

Adding Buttons and Test

Now that the class can support file loading and saving, all that remains is to add Load and Save buttons to the main form of the program and to call these methods. In the spirit of true laziness, I’ve hard-coded a filename just to test out the functionality:

Private Sub cbSave_Click(ByVal sender As System.Object, _ 
ByVal e As System.EventArgs) Handles cbSave.Click 
oCell.SaveToFile("c:\fred.lif") 
End Sub 
Private Sub cbLoad_Click(ByVal sender As System.Object, _ 
ByVal e As System.EventArgs) Handles cbLoad.Click 
oCell.LoadFromFile("c:\fred.lif") 
oP.Invalidate() 
End Sub 

To verify that the save/load works, fire up one of the automata variants, let it run through a few dozen cycles, then hit the Save button. You can take note of two or three patterns in specific spots on the board so that you can identify them later. Then, resume the current pattern and let it change drastically from the save point. Finally, hit the Load button and see the board state magically resume back to its saved state.

The ability to serialize complex objects to disk is a great quick way to implement a save/reload feature for your games without having to write line-by-line I/O code.