Page Flipping & Handling Input

Introduction:

One of the topics I've not covered yet is page flipping and page blitting. Although if you've followed my previous articles you've certainly seen it in action, I haven't really delved into what it's all about. So I'll touch on that in here.

Also, seeing that you'll want a way to get your users to move images and such around the screen, I figured a talking about input devices would be in order. This will cover the fundamentals on keyboard, mouse, and joystick usage. They are super simple to use, so this won't be a massive article.

It's recommended that you download the source code so you can see all this in action.

 

Screen Blit Animation

This form of animation actually allows you to write to the screen's buffer real-time. This used to be the way that games were made, especially at 320x200 resolutions, but it's not as common today. The idea is that the monitor is refreshed n number of times per second. If we catch the monitor half-way through it's refresh phase, we'll see flickering in our images. This looks bad.

BB provides a function called VWait which basically tells your program not to do anything until the Vertical Sync is on. In other words, when the monitor has completed refreshing the screen and is going back up top to start it's refresh again, BB will know this and will tell your program to quickly draw everything.

You can find the full source for Blit animation inside the blitimage.bb file, but here's the main While loop to show you how it's used:

 


  ; loop until the player hits the ESC key
  While Not KeyHit(1)

    ; check to see if the arrow keys are pressed
    CheckKeys()

    ; Tell BB to wait for the Verticle Retrace
    VWait

    ; clear the Screen
    Cls

    ; draw the current Ship Frame on the center of the Screen
    DrawImage(imgShipFrames(iCurrentFrame),iShipX,iShipY)

  Wend
  
  ; end the program, return control to Windows
  End

Page Flip Animation

Page flipping is utilized most often in today's games because it's a way to ensure you're not going to get flicker. The concept is to have a piece of memory set aside (preferably video memory, for speed reasons) that is laid out exactly like your primary video memory (or screen buffer). So, you'd have a primary (front) buffer and a secondary (back) buffer. The back buffer, again, is simply a duplicate layout of the primary buffer. The primary buffer is also known as the front buffer.

Now, the idea is that while your front buffer is displayed to the user you are busy drawing on the back buffer. This way you are not drawing anything to the main screen while the user watches. When you have completed your drawing you simply Flip the two pages. So, now your front becomes your back buffer and your back buffer becomes your front. Since this simply tells the video card to point to a new place in video memory when doing it's refresh, it's instant. Plus I'm sure that BB (or more likely, DirectX) makes sure the Verticle Retrace is accounted for when doing the Flip.

There are three functions that you'll need to use in order to do page flip animation. They are the SetBuffer, BackBuffer, and the Flip commands. The SettBuffer command tells BB where to draw. BackBuffer returns a pointer to the back buffer we discussed above. The Flip command tells BB that we're ready to swap our front and back buffers.

You can find the full source for Page Flip animation inside the flipimage.bb file, but here's the set up code and the main While loop to show you how it's used:

 


  Const iScreenWidth=800               ;what's our screen width?
  Const iScreenHeight=600              ;what's our screen height?

  ; Initialize BB's Graphics using our constants
  Graphics iScreenWidth, iScreenHeight

  ; Set up our primary drawing surface to be the BackBuffer
  ; this is to allow for page-flip animation
  SetBuffer BackBuffer()

  ; loop until the player hits the ESC key
  While Not KeyHit(1)

    ; check to see if any keys were hit
    CheckKeys()

    ; clear the BackBuffer
    Cls

    ; draw the current Ship Frame on the center of the BackBuffer
    DrawImage(imgShipFrames(iCurrentFrame),iShipX,iShipY)

    ; Flip the BackBuffer to the Display
    Flip
  Wend
  
  ; end the program, return control to Windows
  End

Using the Keyboard

When moving a ship around, firing, etc. you'll want to use a keyboard routine that keeps track of when a key is held down. While you can certainly use the KeyHit command for checking on one-time hit keys such as ESC, you'll need something a little more robust for real-time stuff.

This is where the KeyDown function comes in. All this function does is return a true or false response when asked if a particular key is being held down. In order for you to send it a particular key to check, you'll need to know the scancode. This is a code that the computer recognizes the key by. Generally what I do is find the code (using BB's help area, under "Keyboard Scancodes") and make a constant with a recognizable name. Like this:

 


  ; Setup Keyboard Constants (Arrows, KeyPad Arrows)
  Const LeftArrow=203, RightArrow=205
  Const LeftKPArrow=75, RightKPArrow=77

Now I know that all I have to do is remember "RightArrow", not 205. Makes it easier to read code that way, don't ya think?

The following code is a snippet from the kbinp.bb file. It's a function that I called CheckKeys() and all it does is checks to see if the left or right arrow is being held down and then spins the ship accordingly:

 


  Function CheckKeys()
   If KeyDown(LeftArrow) Or KeyDown(LeftKPArrow) Then
      ; change the current frame (spin it to the left)
      iCurrentFrame = iCurrentFrame - 1

      ; if the current frame counter is less than 0
      ; reset it to the number of rotations - 1
      If iCurrentFrame < 0 
         iCurrentFrame = iNumRotations - 1
      EndIf
   EndIf

   If KeyDown(RightArrow) Or KeyDown(RightKPArrow) Then
      ; change the current frame (spin it to the right)
      iCurrentFrame = iCurrentFrame + 1

      ; if the current frame counter goes past our number of
      ; allowable rotations, reset it to 0
      If iCurrentFrame > iNumRotations - 1
         iCurrentFrame = 0
      EndIf
   EndIf
  End Function

Using the Mouse

The next device I'll touch on is the mouse. Since the mouse has waaaaaaay fewer codes to worry about, it's a little easier to check on. However, you may still need a little trickery to get things done.

Let's say that we wanted to duplicate our keyboard routine using the mouse. If the mouse is being run to the left, spin the ship left. If to the right, spin it right. This sounds like a simple little thing to do, and it is if you know a tiny trick.

First let's look at the problem. If you check the mouse coordinates using MouseX and MouseY you'll find that they will be locked to the screen resolution. This is cool if you are going to button clicks and such on a screen. But since we want to spin the ship left as the mouse moves left, we'll need to see if the current mouse position is different from the previous check. Since you'll eventually get a MouseX postiion of 0, you'll find after a while that the ship will stop spinning.

To solve this we use the MouseMove function (thanks to "shockman" on the BB messageboards for getting me this function name!) to re-center our mouse after each check. This will ensure that we will always either be equal to, greater than, or less than our last check.

The following code is a snippet from the mouseinp.bb file. It's a function that I called CheckMouse() and all it does is checks to see if the mouse is moving left or right and spins the ship accordingly. Note that the section before Function CheckMouse() is really located at the top of the program, it's here for reference only:

 


  ; center the mouse on the screen
  MoveMouse iScreenWidth/2, iScreenHeight/2
  
  ;Save our mouse position for X
  Global iCurrentMouseX=MouseX()       

  Function CheckMouse()
   If MouseX() < iCurrentMouseX Then
      ; change the current frame (spin it to the left)
      iCurrentFrame = iCurrentFrame - 1

      ; if the current frame counter is less than 0
      ; reset it to the number of rotations - 1
      If iCurrentFrame < 0 
         iCurrentFrame = iNumRotations - 1
      EndIf
   EndIf

   If MouseX() > iCurrentMouseX Then
      ; change the current frame (spin it to the right)
      iCurrentFrame = iCurrentFrame + 1

      ; if the current frame counter goes past our number of
      ; allowable rotations, reset it to 0
      If iCurrentFrame > iNumRotations - 1
         iCurrentFrame = 0
      EndIf
   EndIf

   ; reset the mouse to be at the center of the screen
   MoveMouse iScreenWidth/2, iScreenHeight/2

   ; reset iCurrentMouseX to be at the center of the screen
   iCurrentMouseX = MouseX()
  End Function

Using the Joystick

The final input device I'll talk about is the Joystick. This is probably the easiest of all since it has a function that pretty much does what we want already. This function, JoyXDir, tells us if the X direction of the Joystick is -1 (left), 0 (centered), or 1 (right). That's all we need to do this little diddy. Now you can commands that will allow you to see how far you are pushing the stick in a direction, but for the scope of this tutorial JoyXDir will suffice.

There's really not much to explain with this piece of code. I'm simply checking to see if the stick is being push left or right and spinning the ship accordingly. This is a snippet from the joyinp.bb file.

 


  Function CheckJoystick()
   ; call the JoyXDir() function to see if
   ; the stick is being pushed left
   If JoyXDir() < 0 Then
      ; change the current frame (spin it to the left)
      iCurrentFrame = iCurrentFrame - 1

      ; if the current frame counter is less than 0
      ; reset it to the number of rotations - 1
      If iCurrentFrame < 0 
         iCurrentFrame = iNumRotations - 1
      EndIf
   EndIf

   ; call the JoyXDir() function to see if
   ; the stick is being pushed right
   If JoyXDir() > 0 Then
      ; change the current frame (spin it to the right)
      iCurrentFrame = iCurrentFrame + 1

      ; if the current frame counter goes past our number of
      ; allowable rotations, reset it to 0
      If iCurrentFrame > iNumRotations - 1
         iCurrentFrame = 0
      EndIf
   EndIf
  End Function

Conclusion:

I know that I didn't touch on button-clicks and such with the mouse and Joystick, but if you look under "Input Device Commands" in the BB help section you should be able to start incorporating those easily. Part of getting the game coding and such down is to start tinkering with things on your own, so I purposely avoided covering every input command. Get that hacker mentality going...try things and see what happens! That's all part of becoming a game programmer ;)

As an exercise you should consider putting all of the input functions into one file and check to see if any of them are being used in your main While loop. Also, why not check out the various scancodes to connect to your keys?

Until next time...cya!

This tutorial is by Christian Coders>