Visual Basic Explorer
Visual Basic Explorer
 Navigation
 Home


 Coding
 Source Code

 FAQ Center

 VB Tips

 Downloads

 ToolBox

 Tutorials

 VB Games

 VB News

 VB Award

 VB Forums



 Affiliates
 Planet Source Code

 Rent a Coder

 DirectX4VB


 Misc
 Search

 Feedback

 Advertise

 About


Need to hire
a VB coder?

Please support our sponsor:

 Home 
 Site Map 
 Forums 
 News 
 Feedback 



Doing Tiles in VB

Creating Tile Based Games - Part 1


Name: Doing Tiles in Visual Basic
Author: Søren Christensen
Date: September 21, 1998
Reviewed: August 26, 1999
Level: All
Project: Download the completed sample project.

Credits: This tutorial was provided by Søren Christensen.


Much can be said about Visual Basic, but it has never been the preferred environment for game developers, but with the increased support for DirectX in Visual Basic (rumor has it that Microsoft is developing support for DirectX in Visual Basic even as I speak), this will probably change. But even as DirectX is a big thing, it is not everything when it comes to Windows gaming, especially when we are dealing with Visual Basic. So since the DirectX technologies are not officially supported, I will concentrate solely on using the normal Windows functions in the quest of showing you how tile based gaming can be implemented in Visual Basic.

Before beginning on the actual coding, a brief explanation of some of the technical terms would be appropriate.

API: The Application Programming Interface. In this tutorial the word API refers to the Window Application Programming Interface. This interface consists of all the functions, which can be used to make a Win32 program. You should have a basic knowledge on what the API is, and how to use it in VB. All the specific API functions used in this tutorial will be briefly explained.

Device Context: A device context is a Win32 structure, which defines several graphic attributes for a device (screen, printer or file). All graphics based API calls are made through a device context object, which ensures device-independence and encapsulates the actual hardware drawing.

 

The Gamefunctions.bas module

Before doing any tiling, I believe a review of some of the functions in the GameFunctions module is appropriate. Some of the functions are not used in this project, and will therefore not be reviewed here.

The GameFunctions module contains some generic and quite useful functions for the VB game developer. Most important are the graphics functions, which are used to load graphics into a usable device context, from where it can be blitted. Also quite useful are the keyboard functions, which can be used to test for a pressed key. But enough with the rambles and let’s get down with the technical specifications.

 

The GenerateDC function:

This function is the function I find most useful when working with graphics in VB. It will load a bitmap from file and generate a Device Context for it, so the various graphics functions are able to access it. Not only that, but it also makes is possible to create a game in VB without using a picturebox for loading the bitmap. Not that the picturebox is a bad thing, but it still provides additional overhead when used as a storage for pictures, especially in view of the fact that a normal game would require a lot of picture boxes for even the simplest animations.

The function takes one parameter, a string variable, which is the file name of the bitmap to load.

The first thing to do in creating a DC (device context) is logically to call the CreateCompatibleDC API function, which returns a freshly created handle to a compatible DC. It is important to check the return value from this function, since the handle is essential for the rest of the operations in this function. Simply checking that the value is not less than 1 does this.


DC = CreateCompatibleDC(0)

If DC < 1 Then
    GenerateDC = 0
    Exit Function
End If

 

Though we know have a handle to a device context, the context is still unusable, since it has not dimensions or colors, so we need a handle to the bitmap in order to get this information. To do this we use the LoadImage API function, which will load an image from a file and return a handle to it. The LoadImage function takes a few parameters, which are specified as constants (IMAGE_BITMAP, LR_LOADFROMFILE and LR_CREATEDIBSECTION). These are used to specify that we want to load the bitmap from the file specified in lpsz parameter, and that we want to create a DIB (device independent bitmap). This function does not work under WinNT, at least not with the LR_LOADFROMFILE flag, so beware of this. After calling the function a rudimentary error check is done to see if the bitmap was loaded. If it was not, the previous created DC will be destroyed and the function will return with 0.

Note: The next two lines should be all on one line.


hBitmap = LoadImage(0,  FileName, IMAGE_BITMAP, 
0, 0, LR_LOADFROMFILE Or LR_CREATEDIBSECTION)

If hBitmap = 0 Then 
    DeleteDC DC
    GenerateDC = 0
    Exit Function
End If

 

Now we have a DC and a handle to a bitmap, so know we need to combine these into a usable DC. This is done with the SelectObject API function. This function will select the bitmap into the DC and thereby specifying what we need, the dimensions and the colors for the DC. After this is done, we can return the DC as the workable device context, but we still need to do some cleaning, since we still have the bitmap loaded into memory. It can be deleted with the DeleteObject API function, passing the handle of the bitmap as the sole argument.


SelectObject DC, hBitmap
GenerateDC = DC
DeleteObject hBitmap 

So now a usable DC with a bitmap has been created, but this does not mean that it is all over, far from indeed. Since we have created a DC, with the CreateCompatibleDC function, we also need to delete it again, in order to save resources. This is done with the DestroyDC function in the GameFunctions module.

The DestroyDC function:

As mentioned, this function will destroy a device context created with the GenerateDC function. It is a simple function, which first check to see if the passed DC is valid and then tries to destroy it with the DeleteDC function. It returns the value from DeleteDC function.


If DC > 0 Then
    DestroyDC = DeleteDC(DC)
End If

The Check[Key]Key functions

These functions (5 in all) checks if a specific key is pressed. They all return true if the key is pressed. So what’s the use of these functions, when there is already the Form_Key___ events? Well I’ll explain later in the section, where I discuss the Main Game Loop. So to get back to these Key functions. They are all based on the GetKeyState API function. The GetKeyState function checks if the specific key is pressed and returns an appropriate value that is either -127 or –128. So it is quite simple to check against one of these values and then return the result back to the calling function. (The following code is from the CheckLeftKey function, which is very similar to the rest of the Check[Key]Key functions)


Dim vkLeft As Long

vkLeft = GetKeyState(VK_LEFT)

If vkLeft = VK_KEYDOWN Or vkLeft = VK2_KEYDOWN Then
    CheckLeftKey = True
Else
    CheckLeftKey = False
End If

The rest of the functions

The GameFunctions module contains several other functions than the ones mentioned here. These I will not dwell further into in this article. So use them if you want to or leave them be.

 

Let’s Tile!

Now that you have a basic idea on how the graphics system works in this sample program, we’ll move into the world of tiling. But as a last warning to you, then all the information I present is the result of my own research and doings, and as such the code is not optimized. So if you know of any better way to do a routine than the one I have made, please let me know.

Creating a tile-based game is much more than just tiling several bitmaps over a form, it is also control of movement, position and scrolling. If you just need a playground that is permanent and non-changeable then use a permanent and non-changeable background graphic, do not use tiles.

In this tutorial I will show you how to create a basic smooth scrolling tiling background, and move a sprite over it. The graphics are quite simple (bordering to the ugly), but you’ll have to live with that, since I am an absolute nightmare when it comes to creative graphics making.

Basically the tiling background consist of 8 tiles, stored into a single bitmap (tiles.bmp). To each tile there is a special type structure. This structure is defined as follows:


Type Tile
    SpecialData As Single 
    SpecialData1 As Single 
    SourceX As Integer 
    SourceY As Integer 
    TileWidth As Integer
    TileHeight As Integer
End Type

This structure contains several very important members. But why use a structure anyway? Well since a tile is much more than (as I have stated above) just graphics, it is only natural to use a structure as storage for all the extra information a specified tile might have, this way you are both able to identify a tile in the source bitmap, but also identify the special effects a tile might possess. The tile structure I have used in this article is quite simple, in a way that it only describes the most needed things. These things are first and foremost the dimensions and location of the tile in the source bitmap (SourceX, SourceX, TileWidth & TileHeight). Then there are two additional members, the SpecialData and SpecialData1 members. These members describe a special kind of action or condition that the tile is responsible for. In the sample project only the SpecialData member is used, and it is defined as a speed factor, which will be multiplied by the static speed of the sprite, more on this later.

So now that you have a basic idea on how we use and store tile data, it is time to move on and see how this is used in the sample project.

 

Initialization

Before diving into the deep end, take a look at the project settings or more specifically the properties of frmTile in the sample project. The form is a bare form with no borders or control box. The form also has its AutoRedraw property set to true, the ScaleMode set to pixels and lastly the StartUp position set to center screen. The width of the form is 160 in pixels and the height is likewise 160 pixels. The tiles used are each 16x16, which means that we can have 100 visible tiles on the form at any time. Remember this!

All the initialization is done in the Form_Load event.

The first thing to do is to get the basic layout of the whole gaming field (not just the visible part). This information must be stored somehow, so that it can be used every time the program starts. We could store the information in an array, which is initialized on start, but this makes the program very inflexible and much less fun to do. So the obvious choice is of course a file. The actual information, which must be stored, is not tile information, but instead just simple numbers, which acts as an identifier to the actual tile. The point is to read the whole file into a 2-dimensional array. On a graphical illustration it could look like this:

This way we now have the whole gaming field area in memory and accessible. The brilliance in using this technique is that at any given time you can directly calculate what tile is located at a given (x, y) coordinate, simply by dividing the coordinates by the tile width and height. More on this later.

In the sample project the function doing the file reading is located in the GameFunctions module and is called ReadTileFile. The first thing the function starts out with is to count the lines of the file. Then it reads the file line by line, and converts all the characters in the file to numbers and stores them in the 2-dimensional array. Well if the reader is a bit awake now, he will probably be wondering what makes this method effective and efficient to use, since only 10 different tiles (0-9) can be store in this way. Well, nothing actually, the only good thing about this function is that it is simple and easy to understand. If this was a real life application, the only way to go was of course to use a binary file, and read each tile as a byte. This way you can store up to 256 different tiles in a file. But as stated, I have used this function for simplicity, and it is also sufficient since only 8 different tiles will be used in the sample project.

So now that the whole gaming field is stored in the array as numbers to specific tiles, the tiles need to be defined. This is done is the function DefineTiles, which is located as a private procedure in frmTile. Each of the eight tiles used is defined by setting the members of the tile structure. The eight resulting structures are stored in an array, TileDesc (short for tile description). The first set of members to define is the location of the graphics for the tile. In this application we have all the tiles in one single file, tiles.bmp. If you have several graphic files, a new member to sprite structure could perhaps be a long variable, describing the Device Context where the graphic is.

We have two dimensions of tiles in the graphic file (2 rows with 4 columns), but we only have a 1-dimensional array, so we somehow need to convert this. This is done by doing a little bit of math and using two For-Next loops. The first For-Next loop defines the rows of the graphic file, which we have two of. The second For-Next loop defines the columns of the graphic file, which we have four of. So the following code snippet will run 2 x 4 times:


For I = 1 To 2
    For J = 1 To 4

So now that we have enough loops to fill each member, we need to convert the counting into a 1-dimensional array index. Using a bit of logic and math does this:

TileDesc((J - 1) + (I - 1) * 4)

The first parentheses (J-1) ensures that we start at index 0 in the array (remember that the arrays we use are zero-based.). The next part to add is the actual dimensional converter (nice name), it factors the number columns with the current row. This ensures that with the first row, we are adding (1-1)*4, which is 0, to the (J-1) value, ergo we are getting the indexes of 0 to 3 (4 in all) in the first outer loop. In the second loop we get (2-1)*4, which is 4 to add to the (J-1) value, this will result in the index of 4 to 7, exactly what we need.

In pseudo code this could be describe as:

For Row number 1 to the last Row

For column number 1 to the last column in the row

Index = Column + Row number * number of columns

Next Column

Next Row

Confused? Well then perhaps this brilliant graphical representation might clear things up:

 

As you can see we simply move each row into a straight line of array index. Still confused? Well go back and start over. J

So now that we have a way of indexing each tile, we need to set the data members. This is done inside the loops of course and looks like this:


TileDesc((J - 1) + (I - 1) * 4).SourceX = (J - 1) * TileWidth
        TileDesc((J - 1) + (I - 1) * 4).SourceY = (I - 1) * TileHeight
        TileDesc((J - 1) + (I - 1) * 4).TileWidth = TileWidth
        TileDesc((J - 1) + (I - 1) * 4).TileHeight = TileHeight

The first member being set is the X-coordinate of the tile on the graphic (remember that he X-coordinate is the left most X-coordinate of the tile). As we start from point 0, we subtract 1 from the current column count (J) and factor this with the constant width of a tile. This will give values of 0, 16, 32 and 48 (TileWidth = 16), which is exactly the left most X-value of each tile. The same thing is going on in the next line where the Y-coordinate is set (again the Y-coordinate is the uppermost Y-value on the tile). In the first loop all Y-coordinates will be 0 (1-1*TileHeight) and in the second loop they will all be 16 (TileHeight = 16). The next two members to set is the width and height of the tiles. These values are constants so no calculation is done, they are simply put into place.

After all the looping is finished, we set the SpecialData member of the tiles. This member specifies the factor that the speed of the sprite will be modified with. As you can see some tiles will be very fast to move over, some very slow and one non-moveable. This is a comparatively a simple effect, but does serve its purpose of demonstrating the use of effects in tiles. More on how this member is accessed and used later in the tutorial.

The next thing to do is to load the graphics and set them up. This is done by the GenerateDC function in the GameFunctions module. We have three graphic files to load, the Tiles, the Sprite and the Sprite mask. The reason for using a sprite mask and not creating one through the Win32 system, is that the colors and of the masked sprite does not allow for a very good mask created on the run.

After loading the graphics the initial starting point for the sprite, the X and Y coordinate are set, and then the game loop is started.

So the sum up, the following things were completed in the initialization of the project:

  • The Gaming field was read into memory from the tile file.
  • The tiles were defined, both the special effects and the location on the graphic file
  • The graphics was set up and loaded into a created device context

 

The Main Game Loop

Everything is set up, and ready to go. But before doing any other thing, a quick discussion on game loops is appropriate. Now in all the gaming books and in many gaming tutorials I have seen the main game loop is inside a Timer control’s Timer_Event. Now that is a really horrible thing to do and I will personally slap anyone who uses this method hard on the fingers. The Timer control has a really poor resolution of only 50 ms, which mean that you can only have a frame rate of 20 frames pr second (fps). Now this is not really that bad, but it gets bad when you consider the very poor consistency of the event call, which makes the sprites wobbly and jumpy. Not very good! Another and often used scenario is to detect key presses in the Form KeyPress events. This is OK for normal applications, but not very usable in games, because the key presses will suddenly, after a brief pause, start to repeat very quickly. This makes sprite movement look bad, as they will move just a little, pause and then move away quickly. This can of course be overcome by making some API calls and set the pause interval. But yet another, and probably more serious problem, is that this scenario only allows for one key press at the time. So in order to detect multiple pressed keys, you have to set your own state variables and check these in the events. Nay my friend events are not necessarily a good thing when it comes to games. So what can be done then?

Well we’ll use a do-loop scenario and slow the loop with a call to the Sleep API. This way we have complete control on both the key presses, the timing and speed of the application.The Game loop in the sample application looks like this:


Private Sub RunMain()
Dim CurrentTick As Long
Dim LastTick As Long

Const TickDifference As Long = 10
Me.Show

Do
    If IsEscapePressed Then
        EndIt
        Exit Do
    End If
    
    CurrentTick = GetTickCount()
       
    If CurrentTick - LastTick > TickDifference Then
                
        AdjustForTiles
        UpdateKeys
        DrawTiles
        DrawThing
        Me.Refresh
        LastTick = CurrentTick
        
        DoEvents
    
    Else
        
        DoEvents
        Sleep 2
    
    End If
Loop

Unload Me
Set frmTile = Nothing

End Sub

As you can see it is a procedure that never ends, until the program stops executing. There are two variable declarations in the procedure, each having something to do with storing tick values. Likewise there is a constant TickDifference, which defines the amount of ticks between each frame. The API for this timing scenario is the GetTickCount function, which returns the number of milliseconds since Windows was started. The actual value returned by the function is of no meaning to us, since we use the return values as relative values. The idea is first to store the current tick count in the CurrentTick variable. Then a check is made to see if this count has more than ten ticks, since the last count, which is stored in the LastTick variable. If the count has more than ten ticks, then the allowed time interval has passed and the loop fires the drawing and other game functions. If the count is not more than ten, then we are not allowed to anything else than sleep, so the Sleep API is called, which pauses the process (application) for a given amount of milliseconds. This way the application will only execute the actual game functions at best with intervals of 10 milliseconds. This will give you a frame rate of 100 fps, which more than acceptable.

OK enough with the loop structure, and let’s concentrate on the actual game functions.

The first function to be called is the IsEscapePressed function, which returns true if the escape key is pressed. The function returns true if the Escape key is pressed. The function is one of the key press functions defined in the GameFunctions module.

The next thing to happen is a call to the GetTickCount API function, then the return value is checked against the last call to the function. If the check passes then the actual core game functions are called.

The first function to call is the AdjustForTiles. This function services the ‘special effects’ in the application, which as stated previously is variable speed. The function is very simple, it first calculates the tile the sprite is located on. Dividing the coordinate values with the width and height of the tiles does this. This gives a direct index to the TileArray array structure, which as we know contains the Gaming field represented in tile numbers. So now that we know this, we can use this information to get the Speed modifier for the specific tile. This is done by accessing the TileArray with the two indexes we have just calculated, and use this number as the index to the TileDesc array, which contains the actual tile descriptions. This way is very fast and efficient. It looks like this:


CurrentTileX = Int(X / TileWidth)
CurrentTileY = Int(Y / TileHeight)

TempThingSpeed = ThingSpeed * TileDesc(TileArray(CurrentTileY, CurrentTileX)).SpecialData

The speed value, which is calculated by multiplying the constant speed and the factor defined in the tile structure, is stored in the TempThingSpeed variable. This variable is later used when we update the position of the sprite.

The next function that is called is the UpdateKeys function, which deals with the user input.

The basics of this function is to update the two coordinate values (X, Y), which are used as the drawing point of the sprite. This basically means that the function must ensure correct movement of the sprite. Correct Movement imposes some restrictions on the function, since we have already defined that a speed factor of 0 on any tile means that the tile cannot be moved into.

The function is divided into four blocks of code, each taking care of one of the directions (up, down, left and right). The code for each block is almost identical, so I will only work with one block here (the one dealing with Left arrow key presses). The first thing to do before any of the blocks are executed is to store the current X and Y values. The reason for this will become clear very soon.

The first thing any block of code does is to test if the given key is pressed. A call to one of the specified Check[Key]Key functions defined in the GameFuntions module does this. The function returns, as we already know, true if the key is pressed. If the key is pressed then adding the TempThingSpeed variable with X and the MinimumSpeed constant will update the X coordinate. But as you can see there is also something else going on, since we are using the Int function to round of the addition of TempThingSpeed with 0.5. The reason for this is that we need a rounded value for the X coordinate and we needed rounded of correctly. The MinimumSpeed constant is added so we do not risk stopping the movement.

Now that we have the new X coordinate, we need to check if the sprite can actually move into the tile. This is done by determining whether the new X value resulted in a tile change, which is done by simply calculating which the new X value is in. If it has changed we need to determine if the new tile can be moved. This is done in the same way that we found the current speed modifier in the previous function. We use the tile number as an index to the TileArray and use the value from that array as an index to the TileDesc array. If it happens that the SpecialData member of the new tile is 0, then we reset the X-value to the value it had before, which effectively makes it appear to be stopped. The same thing is going on in the other blocks of code, except the values are changed to fit the key press.


If the reader is still awake, he might be foaming with objections and protests now, because this function resets the X coordinate to the original coordinate, which means that the sprite might actually not be even close to the tile which made it stop. This situation is illustrated in the following figure:

In illustration 1, the sprite has not been moved yet. In 2 the new coordinates of the sprite has been calculated, but resulted in coordinates that will cause the sprite to move into a non-moveable tile. Therefore the coordinates are reset to the original values (Nr. 3). As you can see this means that a certain area between the moveable tile and the non-moveable tile can not be moved into. This will cause the sprite to be stopped a small distance from the non-moveable tile, and the results are bad animation.

The solution for this problem can be to iterate from the new X coordinate and downwards to the original X coordinate, and in each iteration calculate if the sprite has moved back into the original sprite, and stop it there. But this will not be covered further here, since this is still an introductory article to tiles.

After calculating the new position of the sprite, it is time to do the drawing. The first graphics to be drawn are of course the tiles, since these will serve as the background. The tiles are drawn in the DrawTiles function.

Before examining the code, let’s consider the situation a bit. We have a gaming field of 20x20 tiles as defined in the tiles-text file, but we only have a window of 10x10 tiles. This of course means that the whole gaming can not be displayed at any given time. So how do we implement this? One possible implementation is to draw the whole field in a background DC and then blit the portion, which should be displayed, onto the window. Though this scenario is good for small tiling applications it is most surely not optimal in application which have a very large gaming field, since it will consume a lot of resources. The second possible implementation, and the one that is used in the sample program, is to only draw the tiles, which are visible and forget about the rest. This way we save resources, and only lose a small amount of time, well actually we lose quite a lot of time, but still not more than it can make up for the lose of resources. Another thing worth mentioning is the movement of the sprite. The sprite must be centered on the screen, unless it is of to one of the sides. This makes for some interesting animation routines, since we must take notice of where the sprite is, what 100 sprites we have to draw etc.

So let’s jump into it.

The first thing to do is to calculate the offset to the sides. The offset is used to determine the exact starting for drawing a tile on the sides. The offset value is only relevant when the sprite is out in the middle of the gaming field, where there is no fixed point for the tiles on the sides.

Another value which is very important is the left most column of tiles to draw, or the LowerX variable. This is used to determine where the drawing should start in the TileArray. When the sprite is out in the middle of the gaming field the LowerX is calculated by determine the tile which is on the sprites position and then subject the half value of the total numbers of tiles on the display window, since we assume that the sprite is in the center. Same thing goes when calculating the top and bottom sides of tiles.


If X > FormWidth / 2 And X < FormWidth * 1.5 Then

    LowerXOff = X Mod TileWidth
    LowerX = Int((X / TileWidth)) - TotalTilesX / 2

ElseIf X >= FormWidth * 1.5 Then

    LowerX = TotalTilesX

End If


If Y > FormHeight / 2 And Y < FormHeight * 1.5 Then

    LowerYOff = Y Mod TileHeight
    LowerY = Int((Y / TileHeight)) - TotalTilesY / 2

ElseIf Y >= FormHeight * 1.5 Then

    LowerY = TotalTilesY

End If

The next thing to do is to calculate the upper most tile on the display window. This is very simple, since we now what dimensions the window has (10 tiles) so this is just added to the lower-most tile.


UpperY = LowerY + 10
UpperX = LowerX + 10

So everything is set up and it is time to draw the tiles.


For I = LowerY To UpperY
    For J = LowerX To UpperX
        BitBlt Me.hdc, (J - LowerX) * TileWidth - LowerXOff, _
        (I - LowerY) * TileHeight - LowerYOff, TileWidth, TileHeight, _
        TileDC, TileDesc(TileArray(I, J)).SourceX, _
        TileDesc(TileArray(I, J)).SourceY, vbSrcCopy
    Next J
Next I

Well as you can see, we actually draw 11x11 tiles, and not the 10x10 we had calculated for This is needed because of the possible offsets we might get. We loop through each row with the outer loop, and through each column with the inner loop. We use the I and J values as the index to the TileDesc array.

When the tiles has been drawn, the only thing we need to draw is the sprite. The sprite is drawn in the DrawThing function. The important ‘thing’ in this function is to determine the position of the sprite on the display window. If the sprite is not centered, i.e. is off to one of the sides, we have to move on the display window, else if the sprite in the middle of the gaming field, the sprite is not moved in the display window, only the relative coordinate values are changed.


If X > FormWidth / 2 And X < FormWidth * 1.5 Then
    TempX = FormWidth / 2
ElseIf X < FormWidth / 2 Then
    TempX = X
Else
    TempX = FormWidth / 2 + ((FormWidth / 2) - (FormWidth * 2 - X))
End If


If Y > FormHeight / 2 And Y < FormHeight * 1.5 Then
    TempY = FormHeight / 2
ElseIf Y < FormHeight / 2 Then
    TempY = Y
Else
    TempY = FormHeight / 2 + ((FormHeight / 2) - (FormHeight * 2 - Y))
End If

BitBlt Me.hdc, TempX, TempY, ThingWidth, ThingHeight, ThingMask, 0, 0, vbSrcAnd
BitBlt Me.hdc, TempX, TempY, ThingWidth, ThingHeight, ThingDC, 0, 0, vbSrcPaint

So the first thing to check is to see if the sprite is off to one of the sides, if it is then we just center it on the display window. If it is of to one of the sides, we calculate the point on the display window, based by relative proportional values of the display window. The same thing goes with the top and bottom sides of the display window (the Y-coordinate). The last thing to do is to draw the sprite. We first blit the sprite mask onto the field, with the vbSrcAnd constant as the drawing method. The we blit the normal sprite with the vbSrcPaint constant, and we have a true round sprite.

 

So what’s next

Well this is basically it, or rather this is the end of a basic introductory tutorial on drawing tiles. There is plenty more things to consider and do if you were to make a tile based game, and you could also shine up on this tutorial, since it still misses quite a lot of the more advanced techniques (on purpose). One these misses is concerning the X and Y coordinate, they determine the upper-left most point of the sprite, but in the sample project we use it as point for calculating sprite movement. This is most surely not ideal, since, and as you have probably seen, this makes the sprite able to move a bit over the non-moveable squares. So what can be done? Well simply use the X and Y coordinates as they are and add or subtract the width or height of the sprite, when calculating the movement, but remember that this is different, depending on the direction the sprite is travelling.

This concludes this tutorial, I hope you learned something, if not then start over and examine the code in details, you should be able to pick it up.

Comments and the like are welcomed at: soren@VBExplorer.com


Distribution of this document:

This document (meaning this text and the sample project) may not be distributed commercially or for free in any version, neither hard copied, electronically or otherwise, unless a permission is obtained from the author soren@VBExplorer.com.



Home | About | What's New | Source Code | FAQ | Tips & Tricks | Downloads | ToolBox | Tutorials | Game Programming | VB Award | Search | VB Forums | Feedback | VBNews | Copyright & Disclaimer | Advertise | Privacy Policy |

Quick searches: Site Search | Advanced Site Search 

Copyright 2002 by Exhedra Solutions, Inc.
By using this site you agree to its terms and conditions
VB Explorer and VBExplorer.com are trademarks of Exhedra Solutions, Inc.