Now, we know how to write on our surface and we know how to draw a picture from a file. There are a few more things we can do to make things run smoother. Of course, we would like to be able to update the screen so that we don't just get a big picture on the screen that disappears very easily.
So, first, we are going to design a display loop (game loop) so that our picture is redrawn as fast as possible. We can do this since, in our drawings, we are waiting until the monitor is looking. After we have drawn something, the monitor has to refresh, and during this time, the monitor isn't looking, so our drawing will be done in vain. Waiting for the monitor to look allows us to draw efficiently... and moving the drawing inside of a display loop makes it draw quickly. Quickly and efficiently, two magical words. Anyway, let's get to the point. First we need to make a display loop. This loop will be unrestricted, but exitable. First, we need to add another variable declaration:
'We need to add another
variable to the declarations list.
'Add this one after our PictureSf.
Dim Ending
As Boolean
'Which will determine if it is time for the program to
end.
Now, we set up the loop:
'After we check if the
main surface is not Nothing.
Do Until Ending
'This loop will exit once Ending = True
Add Drawings here.
Application.DoEvents()
'Without this, our loop would never do anything. It
would just add the stuff on its
'to do list, and eventually the program would die of overwork.
Loop
Now, that was simple :P. It will redraw
the screen everytime the monitor is looking. It's not inefficient to draw
this much; in
fact, it's how DirectX was designed... for lots of drawing. Graphical
intensity is more than just one picture bashed onto the screen. Now, what's the
catch? That was too simple. :) It's not really a catch, it's
just something that I didn't mention.
With all DirectX programs, when you tab out of the program or press your Task
Manager escape sequence to exit, the device will have the courtesy to reset your
screen size to its regular Windows size (unless the device croaks somewhere
during execution). This action is nice for you, but our poor device is
utterly shocked when its window is no longer focused. As a result, all of
the surfaces are dropped on the floor and no longer useful (except the display
surface and backbuffer, which are thrown off somewhere to the side). This
sounds like a disaster... what to do? We need to restore the surfaces.
Of course :P.
'Let's modify the old
loop a bit: use this instead.
'After we check if the main surface is not Nothing.
Do Until Ending
'This loop will exit once Ending = True
If Dev.TestCooperativeLevel()
Then
'We can use this to see if our device is all right.
If not, then this will be false
'indicating that a window popped up and now, we need to restore the surfaces
when
'the devices comes back to consciousness.
'Of course, if it is True, then we keep drawing as usual.
Add drawings here.
Else
'First, wait until device receives consciousness.
Do
'We will let this program live and check it two times
every second.
Application.DoEvents()
System.Threading.Thread.Sleep(500)
Loop Until Dev.TestCooperativeLevel()
'Until our device is back to life.
'Here's where we play Doctor. :)
Dev.RestoreAllSurfaces()
'You thought I was joking when I said that we'd restore
all of the surfaces? Heh heh.
'Yes, this only makes the device pick up the main surface and backbuffer, which
are
'still intact. We still need to restore the other surface.
PictureSf = Nothing
'It's broken, so we can't use it anymore... we need to
make a New one.
PictureSf = New Surface("n7.bmp", surfDesc, Dev)
'Now, we can use it again.
End If
Application.DoEvents()
'Without this, our loop would never do anything. It
would just add the stuff on its
'to do list, and eventually the program would die of overwork.
Loop
OK, that part is done, now we need to actually exit the loop when we click on the form. Very simple... almost too simple. :P Just modify our existing Click event procedure.
Private Sub
Form1_Click(ByVal
sender As
Object,
ByVal e
As System.EventArgs) _
Handles
MyBase.Click
Ending = True
'This will end the loop so it doesn't sit and gyrate
around in memory.
Me.Close()
End
Sub
And that's everything. We have now set up an insurance policy for our device so that it can continue to do the job that it does so wonderfully, and ended our loop. :)
Summary of what we have done:
Imports
Microsoft.DirectX'After the Inherits
System.Windows.Forms.Form
Dim Dev
As Microsoft.DirectX.DirectDraw.Device
'Handles the drawing of the things that you want to be
drawn.
Dim MainSf
As Microsoft.DirectX.DirectDraw.Surface
'What you will see on the screen.
Dim BackupSf
As Microsoft.DirectX.DirectDraw.Surface
'This will function as the 'Vice President, where the
MainSf is the President.
'This surface will have the data that the President will display to the screen.
'It does this by flipping. More on that later.
'New surface PictureSf added below... this will contain
the picture that we have
'in the file.
Dim PictureSf
As Microsoft.DirectX.DirectDraw.Surface
'We need to add another
variable to the declarations list.
'Add this one after our PictureSf.
Dim Ending
As Boolean
'Which will determine if it is time for the program to
end.
Windows Form Designer
generated code
'This goes right
after the declarations above. Read along to see what's happening.
Try
Dev =
New Device
'We'll
need to do this as soon as possible. :)
Me.Visible
= True
'It would be
counterproductive to exert all of our efforts
'onto an invisible viewing area.
Dev.SetCooperativeLevel(Me,
CooperativeLevelFlags.FullscreenExclusive)
'The device is
now prepared to start a fullscreen display for you.
'Exclusive means we're going to
violently scratch at any window that
'pops up while the device is doing
the fullscreen :).
Dev.SetDisplayMode(640, 480,
32, 0, False)
'The device
will now kick the screen into 640x480 mode with 32-bit color
'mode (lots and lots of color mode).
0 and False are there to keep
'Dev happy. :)
'Now, we'll fill out our surfDesc 'application form' for our Main surface.
surfDesc.SurfaceCaps.PrimarySurface = True
'Do we want it
to be the main surface which appears on the screen... Yes.
surfDesc.SurfaceCaps.Flip =
True
'Do we want
stuff to be flipped onto this surface? Yes.
surfDesc.SurfaceCaps.Complex =
True
'The
description for Complex was scratched out, but I checked it anyway.
surfDesc.BackBufferCount = 1
'Fast Food:
Would you like backbuffers with that? Just one, please.
MainSf = New Surface(surfDesc, Dev)
'Our device Dev
now looks over our surfDesc 'application form' to make
'sure we filled it out correctly...
and then it gives us a surface if it
'worked. Yes, the Backbuffer
came with it and I don't need to contact the
'manager about my surface. (Thank you
for shopping at DirectDraw9)
'OK, now we've got our surface... we can't just reach in and grab our
'backbuffer
'we need to find a pen cap so that we
'can clip the backbuffer onto to the
main surface. Look... we can use sC!
sC = New
SurfaceCaps
'Now, we can
set it to clip to a backbuffer.
sC.BackBuffer =
True
'With this cap,
we can clip it directly to the main surface.
BackupSf =
MainSf.GetAttachedSurface(sC)
'And, the
backbuffer that we ordered with the main surface is now clipped
'onto the main surface. Clip!
'Let's make it yellow.
BackupSf.ForeColor =
System.Drawing.Color.Yellow
'This will make
all of my little scratches and scrapes on it turn yellow...
'as if the outer coat was removed
(the outer coat is black... once we
'scratch it, part of the outer coat
will flake off and reveal the yellow
'underneath. :P
'After we have set the BackupSf.ForeColor to yellow:
surfDesc.Clear()
'This is where
we get another empty application from the application form
'bin. We can make this empty
since the information we need to store the
'surface is in the file.
PictureSf = New Surface("n7.bmp",
surfDesc, Dev)
'I give the
filename and my empty application form to the Dev. The Dev
'approves all surface creation.
The backbuffer is an exception because it
'comes with the main surface.
'Now, PictureSf is a surface that has
the picture on it. You'll need a
'picture called n7 in the bin folder.
:)
If Not (MainSf Is Nothing)
Then
'We do have to
check somewhere that our 'Application Form' for our
'main surface was approved, right?
'After we check if the
main surface is not Nothing.
Do
Until Ending
'This loop will exit once Ending = True
If Dev.TestCooperativeLevel()
Then
'We can use this to see if our device is all right.
If not, then this will be false
'indicating that a window popped up and now, we need to restore the surfaces
when
'the devices comes back to consciousness.
'Of course,
if it is True, then we keep drawing as usual.
'This goes right before the BackupSf.DrawText call.
BackupSf.ColorFill(Color.Black)
'This will paint the whole form black and get rid of that
'ugly splash of yellow along the top of the display.
BackupSf.DrawText(10, 10, "Hello World!", False)
'I will scratch out Hello World on my backup surface. At this
'point, I
can't see it because the backbuffer is not what the form
'is looking
at... the form is looking at the primary buffer.
'In order to
see the backbuffer form, we must perform a Flip.
'After we have drawn Hello World to the
Backup surface, (before we flip)
BackupSf.DrawFast(200, 200, PictureSf, DrawFastFlags.Wait)
'Draws the surface containing our picture (PictureSf) to (200,200) on the
'screen.
Wait means that we are going to wait until the form is ready
'before we
draw the picture to the surface.
MainSf.Flip(BackupSf, FlipFlags.NoVSync)
'Here's where I flip the Back buffer over and bash it against the Main
'surface.
The stuff on the backbuffer is now transferred to the MainSf
'and the text
is now visible.
'The FlipFlags.NoVSync means that I'm going to wait until the form is
'looking.
Without this, I would just bash it against the surface
'and the
monitor may not notice that I've bashed it... it'll notice
'sooner or
later. If I do this with a game loop, the surface will be
'bashed a bit
too much and you'll actually see me bashing it. (On
'certain
conditions, you won't, like if the whole loop is too slow :p)
Else
'First, wait until device receives consciousness.
Do
'We will let this program live and check it two times
every second.
Application.DoEvents()
System.Threading.Thread.Sleep(500)
Loop Until Dev.TestCooperativeLevel()
'Until our device is back to life.
'Here's where we play Doctor. :)
Dev.RestoreAllSurfaces()
'You thought I was joking when I said that we'd restore
all of the surfaces? Heh heh.
'Yes, this only makes the device pick up the main surface and backbuffer, which
are
'still intact. We still need to restore the other surface.
PictureSf = Nothing
'It's broken, so we can't use it anymore... we need to
make a New one.
PictureSf = New Surface("n7.bmp", surfDesc, Dev)
'Now, we can use it again.
End If
Application.DoEvents()
'Without this, our loop would never do anything. It
would just add the stuff on its
'to do list, and eventually the program would die of overwork.
Loop
End If
Catch ex As Exception
'Exception
catching which we all know and love... or not (especially when
'one occurs :P)
End Try
'End Sub goes below.
End Sub
'After the Form1_Load
event procedure.
Private Sub
Form1_Click(ByVal
sender As
Object,
ByVal e
As System.EventArgs) _
Handles
MyBase.Click
Ending = True
'This will end the loop so it doesn't sit and gyrate
around in memory.
Me.Close()
End
Sub
End Class