|
Doing Tiles in VB
Creating Tile Based Games - Part 2
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.
Introduction
If you have not read the part I of this tutorial, do so now or you will not understand anything I write here. To those who have read part I, then this second part is dealing with some of the things that were left out in the first part. These include Nearing techniques, Correct Centering and More Direction control. I will not discuss using binary files for tile map storage, not because it is not important, but simply because an in depth discussing of these creatures would require both a tool to make them and some additional functions to use them. And as so many others I do not currently have the time. This should not take so long, so let’s get it on.
Center Control
One of the problems in part 1 of this tutorial was that the ’Thing’ was controlled by two coordinates which defined the upper left corner of the sprite. This presents some interesting problems when it comes to hit detection and also when it came to determine whether the Thing could move into a tile or not. A much better way of defining the Thing from two coordinates is of course to say that the coordinates are the center of the Thing. This could be illustrated like this:

OK this is pretty easy when you look at it, but there are several small annoying problems, which must be addressed. The drawing function BitBlt draws a sprite from the upper left corner and not from the center point of the sprite, and our calculating scheme which determines whether the Thing has moved into a new tile is also based on the upper corner coordinates. The way to fix the BitBlt problem is very simple, since we state that the X and Y coordinates are the center of the sprite, so we simply subtract half the height and width of the Thing from the coordinates. This gives us the upper left corner of the thing, and that we can use to draw the sprite. The BitBlt will after this modification look like this:
BitBlt Me.hdc, TempX - HalfThingWidth, TempY - HalfThingHeight, ThingWidth, _
ThingHeight, ThingMask, 0, 0, vbSrcAnd
BitBlt Me.hdc, TempX - HalfThingWidth, TempY - HalfThingHeight, ThingWidth, _
ThingHeight, ThingDC, 0, 0, vbSrcPaint
Now the Thing is drawn as if the X and Y coordinates are the center point of the round thing.
The same technique can be used in the our ‘is the Thing going into a new tile’ calculation. Instead of adding the width and height of the thing to the upper left point (when going down and to the right), we simply add half the width and half the height of the point to the center point, and we are in the same position. But now we also need to subtract half the width and half the height of the Thing when we move to the left and up. This is of course needed as the coordinates now represent the center and not the upper left corner. So the new code to calculate whether the Thing moves into a new tile looks like this:
If Int((X - HalfThingWidth) / TileWidth) <> CurrentTileX Then
And the code to determine whether the tile which, the Thing has moved into is moveable or not will then look like this:
If TileDesc(TileArray(CurrentTileY, Int((X - HalfThingWidth) _
/ TileWidth))).SpecialData = 0
As you can see we subtract half the width of the Thing, to get the (in this case) left most point of the thing.
So now the X and Y coordinates represents the center of the Thing.
Nearing Techniques:
Another important issue, which was left out in the previous part, was the closeness of the Thing to the tiles. As we saw, there was a considerable gab between the tiles and the Thing, usually a distance less than the current speed of the Thing. I suggested a small iteration to close the gap one pixel at a time, and this is also the way we implement here.
The theory is based around a Do Loop structure. No matter the situation we first enter the loop (this is of course based on the fact that the new movement will cause moving into a new tile) and check if the tile is moveable, if it is then we set the Nearing flag to false and exit the loop. If the tile is not moveable, then we either subtract or add 1 to the coordinate in question, and loop again. This is done until the tile has been moved back into the old tile, and then stopped. The code for this sequence looks like this (for Left movement):
If CheckLeftKey Then
X = X - Int(TempThingSpeed + 0.5) - MinimumSpeed
If Int((X - HalfThingWidth) / TileWidth) <> CurrentTileX Then
Nearing = True
Do
If TileDesc(TileArray(CurrentTileY, Int((X - HalfThingWidth) _
/ TileWidth))).SpecialData = 0 Then
X = X + 1
Else
Nearing = False
End If
Loop While (Nearing)
End If
CheckLeft = True
End If
‘Hmmm’ you might say, there must be a better way, and yes there is. We already know the old tile’s position (where the thing will be moved back into), so why not just set the tile after this information. This method will of course be both faster and probably more logical, and you should use it in our situation. But you might also one day be in a situation where the offending or non-moveable tile might actually be partly moveable, and this is the situation where this method is better than the other. This method is just presented here, since it is a bit more complicated than the other, and in this case the better method.
More Direction Control
Well all this is very good, nut we still have major problems, when it comes to detecting if the Thing is over a non-moveable tile. We still only check for this in four directions:

With this scenario, we risk that the ‘corner’ parts of the Thing can be moved in over a non-moveable tile, with out breaking any of our checking rules. One way to avoid this, is of course to check the corners for touching, in much the same way that we checked the ‘non-corners’ part of the Thing, this would look like this:

So this adds four more points. But these points are quite special, since they are never facing the same direction as the direction the Thing is moving in, which also inquires that at least two of the new four points must be checked, no matter which direction the Thing i travelling. How is that? Well let’s say that the Thing was moving left then it could be just under or over a non-moveable (or have one in front, but then it would not be moving) tile, as illustrated here:

As you can see, then if the Thing is moving left, there might be two tiles, which can conflict with its movement, one above and one below the Thing. Also if we had this scenario with the old implementation of tile movement, then Thing would actually be able to move right through the blocking tiles.
So how can we implement this? Well we can set a state flag which indicates what significant movement has taken place, and then use this flag to call the specified checking functions. This is still all done in the UpdateKeys function. So for this four flags are needed, one for each direction. The code for calling the function to do the special checking looks like this:
If CheckLeft Then 'UpperLeft and LowerLeft corners
AdjustLeft
ElseIf CheckRight Then
AdjustRight
End If
If CheckUp Then
AdjustUp
ElseIf CheckDown Then
AdjustDown
End If
Worth noting is of course which extra points that actually need to be checked.
- If the Thing is moving left then the Upper-left and Lower-Left need a check
- If the Thing is moving right, then the Upper-Right and Lower-Right need a check
- If the Thing is moving down, then the Lower-Left and Lower-Right need a check
- And finally you have to guess which points need a check if the Thing moves up.
But there are still some considerations to make before we can implement a method to do this, the most important is how to calculate the relative position of four new points (relative to the center of Thing). Actually this is quite easily done with some basic geometry. Take a look at this illustration:
From this illustration you can see that we need two calculate the length of the intersection between the red lines and the axis of the inserted coordinate system. We know the angle is 45 degrees, and that the radius is 8 pixels. Now an angle of 45 degree is very good, since this must mean that both lengths are equal in size (saves us one calculation). All this we know before the program has even been begun executing, so why don’t we just calculate the desired length before also, and use it in a constant. The length is calculated via the Sine relation:
Sin(45) * 8 = Length
Length = 5.65685 -> 6
We round the number off to get a comparatively near integer constant, which will be easier to use.
So to calculate the four points at run-time we simply either subtract or add this constant to the center point of the Thing (yet another reason for always using center points on your sprites).
Now let’s look at the code that supervises these special points. We have four procedures, one for each direction of movement (as explained above). I will only go through on procedure here (the one for left movement), as there are very similar. The code is like this:
Dim TempX As Long
Dim TempY As Long
Dim Nearing As Boolean
If Int((Y + ThingCornerDist) / TileHeight) <> CurrentTileY Then
TempX = X - ThingCornerDist
TempY = Y + ThingCornerDist
Nearing = True
Do
If TileDesc(TileArray(Int((TempY) / TileHeight), Int(TempX _
/ TileWidth))).SpecialData = 0 Then
TempX = TempX + 1
Else
Nearing = False
End If
Loop While (Nearing)
X = TempX + ThingCornerDist
ElseIf ((Y - ThingCornerDist) / TileHeight) <> CurrentTileY Then
TempX = X - ThingCornerDist
TempY = Y - ThingCornerDist
Nearing = True
Do
If TileDesc(TileArray(Int((TempY) / TileHeight), Int(TempX _
/ TileWidth))).SpecialData = 0 Then
TempX = TempX + 1
Else
Nearing = False
End If
Loop While (Nearing)
X = TempX + ThingCornerDist
End If
As you can see the code is very similar to the code used in the normal UpdateKeys function, with some subtle changes.
The first change is when calculating whether the Thing has moved into a new tile, here we actually use the Y position, even though we move in the X-direction. This is because we assume, or in fact, we are sure of the fact, that the two X positions (The left most X position, and the upper-left X-position) are in the same tile when there is a need for this calculation. But is not sure to assume that the two X points are in the same tile when looking at their Y positions, and of course it is turned around when checking up and down movements.
So if the upper left is actually in a different tile than the left most, then again a check is made to see if the point is in a non-moveable tile, if so then our little Do...Loop is use again, until the point is out of the offending or non-moveable tile. At last, when we have moved (if it was needed) the thing out of any danger, then we simply assign the temporary X value, which was calculated to the X value. The same thing is done with the lower left corner, and the rest of all the corners.
So now you should have a basic idea on what tiles are and how you could implement them. There are many ways to do tiles, some more or less efficient, depending mostly on a specific need. So expect to experience other implementations than mine (don’t complain to me if they are better, just give me an example to copy.J
). Of course if you are serious about doing tile based game you should also look into getting some functions, which lets you read a tile file binary, since this will give more possibilities and options. Another thing worth mentioning is the fact of multi layered tiles, which I have not discussed. Basically instead of just using one layer of tiles, you use multiple layer (hence the name), one for the static background, one for nasty monsters, characters or other miscreants, and lastly one for top level object, which the sprites pass under (trees, birds etc). Perhaps I’ll ramble about this another time.
Well I hoped you enjoyed this, and even better I hope you understood some parts of it, if not well start working with the functions and experiment.
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.
|