Learn VB .NET Through Game Programming [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Learn VB .NET Through Game Programming [Electronic resources] - نسخه متنی

Matthew Tagliaferri

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
توضیحات
افزودن یادداشت جدید





Where to Draw the Die?

As mentioned earlier, the die frame is being drawn to a background bitmap as opposed to right on the form. So when does the background bitmap get transferred to the form? The final solution to this problem came down to some trial and error. My first attempt was to draw the background bitmap directly onto the form, but the result wasn’t adequate. There was a visible flicker as each frame was drawn—in other words, the animation wasn’t smooth.

I didn’t know the immediate answer for controlling this flicker, so I did some research in the Usenet newsgroups via Google (a fabulous programmer’s resource, by the way; I’m not sure how I learned anything new before the Internet became the tool it is today). I found several proposed solutions and tried one or two of them until I achieved a flicker-free result.

The problem had to do with something called a control style that exists for every window handle in Windows (every control, be it a button, listbox, or check-box, has a window handle associated with it). The default control style for a form in Windows manages its own painting, meaning that the operating system is repainting the form and causing the flicker.

Fortunately, there’s a way to override this behavior by changing these control style flags. Furthermore, there’s a way to do this built into the .NET Framework; the Control class (the class upon which all of the user interface classes are built) has a SetStyle method that gives you the ability to manipulate the control flags.

Thinking in object-oriented terms, I decided that it would be useful (for this game and perhaps future games) to have a control that acts as the visible drawing surface for my bitmaps and automatically controls this flicker. I chose the built-in Framework class Panel upon which to base this new control. With only a few lines of code, my new control, the PaintPanel control, was born. The

PaintPanel class acts as the destination for all the bitmap drawing you’ll be doing (see Listing 1-8).

Listing 1.8: The PaintPanel Class



Public Class PaintPanel
Inherits Panel
Public Sub New()
MyBase.New()
Me.SetStyle(ControlStyles.UserPaint, True)
Me.SetStyle(ControlStyles.DoubleBuffer, True)
Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
End Sub
End Class







Note

You can add the code for the new class to an existing code module, or you can keep it within its own code module by selecting Add Class from the solution’s right-click menu.


Once again, you can see the power of object-oriented programming from the standpoint of an existing class and all of its functionality serving as the basis for a new class with different behavior. In this case, a method named New is declared. The New method on any class is a special type of method called the constructor. The constructor is called whenever a new instance of an object is created.

The first thing that the constructor for the PaintPanel does is call the constructor in the ancestor class (the Panel class) using the syntax MyBase.New(). This is an important step that makes sure any initialization code in the Panel class is also executed by PaintPanel class instances.

The remaining three lines call the SetStyle method mentioned earlier. The Me at the beginning of each SetStyle command isn’t necessary in this case, but it’s included for clarity. It specifies that the SetStyle method is attached to the current class (that is, the PaintPanel class). Because there’s no SetStyle present in the PaintPanel class itself (Chapter 2, “Writing Your First Game, Again,” demonstrates this method. The second way to add a new control to a form is through code, which was something you usually couldn’t do in older versions of VB. Listing 1-9 shows the Load event of the fGuess form class.

Listing 1.9: The Load Event for the Form



Private Sub fGuess_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
pnLower = New PaintPanel
pnLower.BackColor = Color.Black
pnLower.Dock = DockStyle.Bottom
pnLower.Visible = True
AddHandler pnLower.Paint, AddressOf pnLower_Paint
Me.Controls.Add(pnLower)
pnLower.Height = Me.Height - lbResult.Height - lbResult.Top - 48
bmBack = New Bitmap(pnLower.Width, pnLower.Height)
'initialize the random number generator
oRand = New Random()
Guess = oRand.Next(1, 7)
Call UpdateGuessButtons()
dieFrame = (Guess - 1) * 6
Dim a As Reflection.Assembly = _
System.Reflection.Assembly.GetExecutingAssembly()
bmxRot = New Bitmap( _
a.GetManifestResourceStream("GuessTheDieRoll.dicexrot.bmp"))
bmyRot = New Bitmap( _
a.GetManifestResourceStream("GuessTheDieRoll.diceyrot.bmp"))
bmStop = New Bitmap( _
a.GetManifestResourceStream("GuessTheDieRoll.dicedone.bmp"))
'initialize the location of the die
diexPos = oRand.Next(0, pnLower.Width - WID)
dieyPos = oRand.Next(0, pnLower.Height - HGT)
DrawDie()
lbResult.Text = "
End Sub



The first portion of this event creates the PaintPanel instance, named pnLower (which is declared earlier in the fGuess class declaration). It sets a few properties, it sets the BackColor, Dock, and Visible properties to some values, and it “wires up” the Paint event to an event handler using the AddHandler statement. This statement is the runtime equivalent of setting up an event handler with the Handles clause at design time and associating an event with some code to be executed when that event fires. Next, the code adds the pnLower control to the form using the Controls.Add method of the Form class (Controls is a property on the form, which itself has an Add method). This step is required to make the form the owner of the new control.





Note

More “free” functionality—the BackColor, Dock, and Visible properties, as well as the Paint event—aren’t declared explicitly in PaintPanel, so they must exist in either the Panel class or somewhere further up the chain. You don’t need to know within which subclass they’re declared to use them here.


The next section of code initializes an instance of a .NET Framework class named Random. This class handles “random” number generation. (Random values on computers are usually pseudo-random values chosen from a predetermined list.) As with all computer-based random values, a seed value gets things started. If a seed value isn’t explicitly given, as in this case, then a seed value based on the current time is used.

Once a Random variable (named oRand) is instantiated, the Next method generates random numbers in any range desired. The first task asked of it is to come up with the initial value of the die, stored in the variable Guess. You’ll notice that the range used here is 1 to 7, even though a die’s range is obviously 1 through 6. The Random variable’s Next method generated integer values less than, but not including, the upper bound given. Later in the procedure, the same Random class initializes the location of the die in a random spot on the PaintPanel. You may receive a runtime exception if your panel is not at least as wide and high as the form-level constants WID and HGT, which are both defined as 144 for this project.

The next part of the event fills the three bitmap variables bmxRot, bmyRot, and bmStop with bitmaps found in the executable. A variable named a is declared (so much for self-documenting variable names) and is initialized to the assembly that’s executing (in other words, the assembly containing this program). The next lines of code load the named bitmaps into the three variables using a method of the Assembly class named GetManifestResourceStream. This method requires a resource name in the format namespace.file.ext. The namespace to use is the name of the project itself. GuessTheDieRoll is the name of this project; the namespace would change if the project had a different name.





Tip

You can add bitmaps and other resources to the project using the Solution Explorer. Once a bitmap is added, you must click it in the Solution Explorer and then switch over to the Properties window to change the Build Action property to Embedded Resource.


The last piece of code that accomplishes drawing the die to the PaintPanel control is the PaintPanel class’s Paint method, shown in Listing 1-10.

Listing 1.10: The Paint Method of PaintPanel



Private Sub pnLower_Paint(ByVal sender As System.Object, _
ByVal e As System.Windows.Forms.PaintEventArgs)
e.Graphics.DrawImage(bmBack, 0, 0)
End Sub



The Paint method passes in the Graphics object that’s associated with the control being painted. As discussed earlier, the Graphics object is the virtual canvas upon which graphics drawing and painting happens. All that’s required in this method is to transfer the background bitmap bBack to the PaintPanel class’s canvas. The 0 and 0 parameters specify to draw the background bitmap in the upper-left corner of the PaintPanel class.

To review, this is the complete chain of events for drawing the die:



The program calculates the coordinates of the animated die source bitmap based on the current frame.



The program selects the correct animated die bitmap based on the state of the die and the direction it’s rolling.



The program clears the background image.



The program draws a single die frame in the correct position on the background image.



The program invalidates the PaintPanel control, forcing the Paint method to be called.



The Paint method copies the background bitmap (containing the die frame) to the PaintPanel class.


/ 106