Now that we have calculated our FPS, we continue where we left off.  With each frame that passes, we can do something (as you saw during my FPS calculator).  The FTotal increased by one for each frame.  Well, we can also do that to objects.  If we increase the X location of an object for each frame, then it will move! :)

Now, we are going to make something move back and forth on the form.  First, let's declare a variable:

'Add this after the FPS variables. :)
    Dim XLoc, dX As Integer
'XLoc holds the x location of the object we are drawing.
'dX holds the speed of the object.

    Dim Msg As String
'This will be helpful if we get an error message.

Next, these values need initialization.

    'In Form_Load, after the caps sC is declared, values are initialized.
    XLoc = 50
    dX = 1
'So, the object starts off at 50 and moves 1 pixel for each frame.
'You can say: location = 50 pixels, speed = 1 pixel / frame, where
'pixels are like meters and frames are like seconds.

    Msg = "No message."
'Since DirectX throws generally indescriptive errors, we can use this to determine
'if DirectX threw an error or if our VB code threw an error.

Then, we alter the drawing line:

If XLoc <= 600 Then
    BackupSf.DrawFast(XLoc, 200, PictureSf, DrawFastFlags.Wait)
'XLoc is the current X location of the object.  200 can remain constant.
'It will only move left to right.

    XLoc = XLoc + dX
'This is an expression which you'll see much more if you go
'into lots of movement on your programs. This one is a regular linear movement.

End If

Now, why did I add that If statement?  Well, DirectDraw will throw you an error if you draw outside of the backbuffer surface (which is the same size as the visible area of the screen).  Speaking of errors, let's update that Msg so we can see errors.

'After we have the Catch ex As Exception
    Msg = ex.ToString()
Finally
    Me.Close()
    Debug.WriteLine("Msg = " & Msg)
'Writes the message to the debug window... along with other stuff, so you really
'have to try to find it.

Back to that If statement.  Notice the value of XLoc increases by 1 every frame, so now, when 640 frames pass, then the value of XLoc will be 690, which is off the screen, and DirectDraw will throw an error (it will give you the error at 608, actually), so that's why I added the If statement.

Well, that's nice, but the image doesn't move back.  I know.  Let's modify the drawing line one more time.

XLoc = XLoc + dX
If XLoc <= 600 AndAlso XLoc >= 0 Then
    BackupSf.DrawFast(XLoc, 200, PictureSf, DrawFastFlags.Wait)
'XLoc is the current X location of the object.  200 can remain constant.
'It will only move left to right.
End If
If
XLoc >= 600 Then
    dX = -1
'Our velocity is reversed!  Now, the xloc will decrease by 1, moving the object left.
ElseIf XLoc <= 0 Then
    dX = 1
'Velocity is now 1 again.  The object resumes moving right like it was at the start.
'of the program.
End If

Now that that is done, our object will oscillate back and forth in a regular way (as a TriangleWave... more on that when I discuss periodic function movement).  It will move from 0 to 60o.  It is recommended that the DrawFast run for each loop, so actually, the If statement can be removed (it's only there for error insurance if you want to play around with values).  The DrawFast should run because you don't want the image to be invisible for a certain frame (that makes a flicker... the regular human eye to brain operating at ~25 Hz might not see it everytime, but I like to make sure my insect-eyed people can enjoy the game as well :) ).  But anyway, that's it!

Summary of what we have done:

Imports Microsoft.DirectX
Imports
Microsoft.DirectX.DirectDraw

Public
Class Form1
    Inherits System.Windows.Forms.Form

'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.
'After our other declarations.
'It's important to know that Environment.TickCount returns time in milliseconds.  So,
'to calculate Frames per SECOND, we need to wait until the Environment.TickCount is
'increased by 1000.  The last value that we've checked will be stored in Counter.

    Dim Frames, Counter, FPS, FTotal As Integer
'Counter will hold the last time we've updated.  When a second (1000 ms) has passed,
'Counter will be re-set, while frames and FPS are updated.
'Frames is going to hold the number of frames drawn since the last update to the
'counter.
'FPS is going to be the value in Frames when 1000 ms has passed.
'FTotal is going to be the total number of frames drawn while the program runs.

'Add this after the FPS variables. :)
    Dim XLoc, dX As Integer
'XLoc holds the x location of the object we are drawing.
'dX holds the speed of the object.

    Dim Msg As String
'This will be helpful if we get an error message.


Windows Form Designer generated code

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As _
     System.EventArgs)
Handles MyBase
.Load
    'Begin.
'Things that we'll need to use here.  This goes at the top of the Form1_Load.
    Dim surfDesc As Microsoft.DirectX.DirectDraw.SurfaceDescription = _
      New SurfaceDescription
'This will hold the description of the surface that we want to make.  We give
'it a nice description of what it can do: be primary, be flipped to, have
'backbuffers, etc. ;) kind of like an application form.

    Dim sC As Microsoft.DirectX.DirectDraw.SurfaceCaps
'I think of these things like pen caps for some reason.  It lets you clip
'another surface to your primary surface... like a pen cap clips onto a suit
'pocket. :)
    'In Form_Load, after the caps sC is declared, values are initialized.

    XLoc = 50
    dX = 1
'So, the object starts off at 50 and moves 1 pixel for each frame.
'You can say: location = 50 pixels, speed = 1 pixel / frame, where
'pixels are like meters and frames are like seconds.

    Msg = "No message."
'Since DirectX throws generally indescriptive errors, we can use this to determine
'if DirectX threw an error or if our VB code threw an error.

'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

'Check our times to see if it is time to calculate FPS.
            If Counter + 1000 < System.Environment.TickCount Then
'If 1000 ms (1 second) has elapsed, then what?
                FPS = Frames   'First, the FPS will be updated.
                Counter = System.Environment.TickCount
                Frames = 0
        '... and the Counter is re-set.  That's all we need to do.
            End If
'Now, we need to progress the frames somewhere.
            Frames += 1
            FTotal += 1
'And that's done.  We can add this before we test the Cooperative level.

            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, "FPS = " & FPS.ToString("G"), False)
              'I will scratch out the FPS 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)
              If XLoc <= 600 AndAlso XLoc >= 0 Then
                BackupSf.DrawFast(XLoc, 200, PictureSf, DrawFastFlags.Wait)
'Draws the surface containing our picture (PictureSf) on the
'screen.  Wait means that we are going to wait until the form is ready
'before we draw the picture to the surface.
'XLoc is the current X location of the object.  200 can remain constant.
'It will only move left to right.
             
End If
              XLoc = XLoc + dX
              If XLoc >= 600 Then
                dX = -1
'Our velocity is reversed!  Now, the xloc will decrease by 1, moving the object left.
             
ElseIf XLoc <= 0 Then
                dX = 1
'Velocity is now 1 again.  The object resumes moving right like it was at the start.
'of the program.
             
End If

              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)

'After we have the Catch ex As Exception
   
    Msg = ex.ToString()
    Finally
        Me.Close()
        Debug.WriteLine("Msg = " & Msg.ToString())
'Writes the message to the debug window... along with other stuff, so you really
'have to try to find it.
   
    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