BillSoo 06-22-2001, 03:18 PM There have been a number of questions about this lately so here is an example I whipped up....
The tiles are 32x32pixels each. There are 256 of them on a picturebox.
I copy them one by one from that picturebox (picTiles) to another picturebox (picMap) which shows 11x11 tiles.
Use the arrow keys to scroll up and down, left and right.
The Code attachment was lost during the forum upgrade. However Squirm was kind enough to reattach it to his post near the bottom.
Bill - June 2002
andrewo 06-23-2001, 08:00 AM hey can you post the code
i only got vb5
Dude, wheres my bong?
andrewo 06-24-2001, 05:37 AM oops heh dont worry got it workin
~
BillSoo 06-25-2001, 11:03 AM I've updated the code to include an animated character....
"I have a plan so cunning you could put a tail on it and call it a weasel!" - Edmund Blackadder
Defiance 06-25-2001, 11:16 AM Hey, I downloaded this example and immediately started zooming through the code. I have a question about some of the variables you had declared. What does it mean when you declare the variable such as c% or r%. I have seen the %, $, and I think a few other symbols used in variable names. Do they do anything special or are they just there as a naming convention?
--Defiance
"Soon I will answer your questions, now you will answer mine..."
BillSoo 06-25-2001, 11:48 AM The type suffixes are a legacy of BASIC.
Back in the old days, you had to declare what type a variable was by using a type suffix:
# = double
! = single
$ = string
% = integer
Nowadays, it's far more common to use hungarian notation (type prefixes) like:
dim iCount as Integer
dim sPath as String
but type suffixes are still valid...
dim iCount%
dim sPath$
and once declared, they are optional:
you could write:
iCount = len(sPath)
or
iCount% = len(sPath$)
I find them useful to save space and make my code a bit more readible (IMO).
For instance, I could have:
dim a as integer, b as integer, c as integer, d as integer, e as integer, f as integer, g as integer
or I could have:
dim a%,b%,c%,d%,e%,f%,g%
which to me, is easier to read...
Some functions, like MID or LEFT, also have type suffixes...
sPath = Left$(app.path, iCount)
is the same as
sPath = Left(app.path,iCount)
The difference is that Left returns a variant while Left$ returns a string. Since you have to convert the variant into a string to stuff it into the sPath string variable, you can save the time to convert by using the Left$ version. It may be a very tiny bit of time, but it does add up.
"I have a plan so cunning you could put a tail on it and call it a weasel!" - Edmund Blackadder
Defiance 06-25-2001, 01:09 PM Ohh, now I understand. I'm a fairly new user to VB (few months) and have only used VB6 so far, so I'm not familiar with the old ways of doing things. OK then, thanks for the reply
--Defiance
"Soon I will answer your questions, now you will answer mine..."
mingyee 06-28-2001, 11:35 AM Hi BillSoo
can i ask you some question on ur attachment file.?
Do you mind explain what's is happening during form_load(), the timer , the drawmap and the others ?
I am confuse with the code but i wish to learn them :)
Please teach me ?? I see the form got many pictures and blue color maps but i don't see any in the running time too?
Thanks
BillSoo 06-28-2001, 01:47 PM OK.....
The Form_Load event creates a random worldmap, creates masks for the characters and initializes the current position.
<pre>
Private Sub Form_Load()
Dim r%, c%, mapID%, tries%
Dim maskclr&
'the map is 200x200
For r = 0 To MAXROW
For c = 0 To MAXCOL
For tries = 1 To 5 'for each tile in the map...
mapID = Int(Rnd() * 94) + 48 '...we pick an image off the
Select Case mapID 'picTiles bmp to put in the map.
Case 94, 95 'For now, we only want trees and
Case Else 'ground (no water) so we restrict
Exit For 'the random number to a range of
End Select '48 to 141 instead of 0-255
Next tries
Map(r, c) = mapID
Next c
Next r
Row = 100 'current position is 100,100 at start
Col = 100
picMap.Move 0, 0, 352, 352 'this is just code to resize the form
Me.Width = 360 * Screen.TwipsPerPixelX
Me.Height = 380 * Screen.TwipsPerPixelY
maskclr = GetPixel(picChars.hdc, 1, 1) 'this code creates the mask
picMasks.Move 0, 0, picChars.Width, picChars.Height 'for the characters
For r = 0 To picChars.ScaleHeight - 1
For c = 0 To picChars.ScaleWidth - 1
If GetPixel(picChars.hdc, c, r) = maskclr Then
SetPixel picMasks.hdc, c, r, 0&
SetPixel picChars.hdc, c, r, &HFFFFFFFF
Else
SetPixel picMasks.hdc, c, r, &HFFFFFFFF
End If
Next c
Next r
picMasks.Refresh
picChars.Refresh
End Sub
</pre>
To make decent animation, you need parts of your character to be transparent.
You can do this by using a transparent bitmap in an imagebox or usercontrol, but since I'm directly painting to a picturebox, I have to use the mask method.
A mask is like a 2 color negative of the image you wish to paint. It's a copy of the image except that all pixels that are the maskcolor in the source are changed to &H0 in the mask (ie. pure black) and all other pixels are changed to &Hffffff (pure white). In the meantime, the maskcolor pixels in the source are converted to &Hffffff as well.
To paint the source to the map, we first paint the mask using the OR operator. This has the effect of cutting a white "hole" in the map the shape of the source. We then paint the source to the map using the AND operator. This fills in the "hole".
It's kind of a weird concept to imagine, but try setting the picChar and picMask visible properties to TRUE and resize the form so you can see them. When you run the program, you should have a better idea of what a mask is....
DrawMap simply uses the current row,col coordinates to draw the map. It does this by using the coordinates to determine which elements of the Map array it should look at. It retrieves these map values and uses them to determine which tile it should copy from picTiles to picMap.
<pre>
Private Sub DrawMap()
Dim r%, c%
Dim x1&, x2&, y1&, y2&, ndx&
y1 = 0
For r = Row - 5 To Row + 5
x1 = 0
For c = Col - 5 To Col + 5
If (r < 0) Or (c < 0) Or (r > MAXROW) Or (c > MAXCOL) Then 'if r,c is out of bounds, draw a black square
BitBlt picMap.hdc, x1, y1, 32, 32, picTiles.hdc, 0, 0, vbBlackness
Else 'otherwise, retrieve the tileindex from the map array
ndx = Map(r, c)
x2 = (ndx Mod 8) * 32 'convert the tileindex to x,y coordinates on the picTile picbox
y2 = (ndx \ 8) * 32
BitBlt picMap.hdc, x1, y1, 32, 32, picTiles.hdc, x2, y2, vbSrcCopy 'copy the tile from picTile to picMap
End If
x1 = x1 + 32
Next c
y1 = y1 + 32
Next r
'picMap.Refresh
End Sub
</pre>
The Timer routine is purely for animation.
It essentially just determines which tile from the picChar (and picMask) bmps should be used, then pastes them to the picMap in 2 operations. First the mask using OR and then the character using AND.
<pre>
Private Sub MoveTimer_Timer()
Static flip As Boolean
Static walkdir As Integer 'nesw
Dim c As Integer
If flip Then
c = CharacterID * 64
Else
c = CharacterID * 64 + 32
End If
Select Case Direction
Case 1: walkdir = 96: Col = Col - 1: If Col < 0 Then Beep: Col = 0
Case 2: walkdir = 0: Row = Row - 1: If Row < 0 Then Beep: Row = 0
Case 4: walkdir = 32: Col = Col + 1: If Col > 199 Then Beep: Col = MAXCOL
Case 8: walkdir = 64: Row = Row + 1: If Row > 199 Then Beep: Row = MAXROW
Case Else
End Select
DrawMap
BitBlt picMap.hdc, 160, 160, 32, 32, picMasks.hdc, c, walkdir, vbSrcPaint
BitBlt picMap.hdc, 160, 160, 32, 32, picChars.hdc, c, walkdir, vbSrcAnd
picMap.Refresh
flip = Not flip
End Sub
</pre>
"I have a plan so cunning you could put a tail on it and call it a weasel!" - Edmund Blackadder
Fizzled 07-02-2001, 06:27 PM What is the purpose of:
For tries = 1 To 5
And what are all those Function declared in General Declarations?
Btw, the character seems to just wrap across the world instead of ever reaching an edge...
BillSoo 07-03-2001, 01:18 AM If you look at the tile bitmap you can see that it has various sections, ie. water, underground, trees etc. I just want to use the land areas with trees so I restrict the random number generator to this area. However, there are 2 weird tiles in this area I don't want, so I have the program try again if it happens to pick one of them.
The functions declared are API calls.
The character will reach the edge of the map, but he starts in the middle of a 200x200 world, so it can take a while....
"I have a plan so cunning you could put a tail on it and call it a weasel!" - Edmund Blackadder
andrewo 07-23-2001, 04:51 AM On a reply above whats MID? is it the middle of the object?
and
what are , are they arrays that you set or something that is made for making graphs?
MAXROW
MAXCOL
~
BillSoo 07-23-2001, 09:30 AM Mid() or Mid$() are functions that return a section of a string. Whereas Left$() gets the leftmost chars and Right$() gets the rightmost chars, Mid can get any chars including those in the middle, hence the name.
It is used like:
substring = mid$(originalstring, iStart, iLen)
where originalstring is, well, the original string to be searched
iStart is the index of the first character
iLen is the number of characters to copy
In my example, MAXROW and MAXCOL are constants that represent the maximum row or column index. It is common, almost universal, to indicate constants by making them all UPPERCASE.
"I have a plan so cunning you could put a tail on it and call it a weasel!" - Edmund Blackadder
Gamer X 07-28-2001, 10:01 PM I see you have put your Case stuff in the form1.keydown, but how do you get it to always have the focus?? When I try to do that, something else has the focus, and the players commands are not recognized. Even if I have form1.setfocus in everything's GotFocus()!
I tried using a textbox instead, but there is a delay when the user holds down a key, between the first and the rest.
Please help!
Gamer X
orufet 07-28-2001, 11:59 PM Make sure the Forms KeyPreview property is set to True
Jacob Sheehy
http://www.sheehy.ca
The more I C, the less I see.
Gamer X 07-29-2001, 06:37 PM Thanks! You really helped me out.
Arigato,
Gamer X
Computer_Guy 07-30-2001, 12:56 AM Okay, you said before about using suffixes to make your variables, and then in a further example of code, you said "&", and didn't specify what "&" actually meant. What kind of type is this?
BillSoo 07-30-2001, 01:06 AM % = integer
& = Long
$ = String
! = Single
# = Double
@ = Currency (I think....)
"I have a plan so cunning you could put a tail on it and call it a weasel!" - Edmund Blackadder
CRSdefiance 08-01-2001, 08:55 PM Is there a limit to the number of tiles you can have on a map?
BillSoo 08-02-2001, 10:48 AM No, not particularly. The Map array is a 2dimensional array of integers, so I would think that you could have a longInt x longInt array... or something like 2 billion x 2 billion tiles.
There is no problem drawing a map that size since only 11x11 tiles are on the screen at any given time.
Now it may be that an array can't be that big. I know that there were much more severe limits in VB3 for instance. But I think that it should be possible.
"I have a plan so cunning you could put a tail on it and call it a weasel!" - Edmund Blackadder
CRSdefiance 08-02-2001, 09:09 PM Okay...that solves that question. Now for my next one... Can the tiles be placed in a predetermined order? I'm quite sure it would be time consuming, but can it be done?
BillSoo 08-02-2001, 10:58 PM I'm not sure what you mean by "predetermined"....
You can create a map yourself I guess and save that. It would simply be an array of integers. You could do it in excel for instance.
Or perhaps you meant the tiles would be randomly generated, but in a particular pattern (ie. water connects to water etc). That would be nice but it would be somewhat tricky to do...
"I have a plan so cunning you could put a tail on it and call it a weasel!" - Edmund Blackadder
CRSdefiance 08-02-2001, 11:09 PM I meant as in going in by hand and placing each and every tile that way..
If I can do it in Excel, you can bet that I'm going to be back asking about that later...lol Let me work up to that one though... :)
I AM determined to learn this...
BillSoo 08-03-2001, 09:26 AM The example program creates a map randomly, but it would be easy to have it accept data from a comma separated file (Excel can create CSV files). If you are really serious about this though, it would be better to write a separate program to create the file so you could "drag&Drop" tiles onto the map. It would be a map edit/creation program.
"I have a plan so cunning you could put a tail on it and call it a weasel!" - Edmund Blackadder
Teric 08-03-2001, 11:18 AM An interesting idea, BillSoo. I think I'll try it...
Teric 08-03-2001, 01:18 PM BillSoo, thanks for your example code with the character moving around on the tile map. You've given me some good ideas.
However, when I downloaded it, the transparency wasn't set quite right; i.e. the terrain could be seen through the character, and there was a black box around the character.
I played around with the program a bit, and I fixed it so that the transparency works. Please take a look.
BillSoo 08-03-2001, 03:01 PM Yours works as well. It appears to be the same as mine though because I did not see the transparency or black border issues you reported....
"I have a plan so cunning you could put a tail on it and call it a weasel!" - Edmund Blackadder
JDYoder 08-08-2001, 03:28 PM This is a very nice piece of work and should be "stuck" to the top of the forum list so it's not lost. I have one question in general about this: Does anyone know how much faster (if at all) this would work if it BitBlt'ed everything to and from memory?
1. Take all the map tiles, character files and masks, and put them into memory DC's
2. For each map refresh, BitBlt all tiles to a map memory DC
3. Then BitBlt the entire map memory DC to the picture box
Does anyone know if this would make a significance speed difference? It's going plenty fast for me as is, but I may want to use this as a base to do something bigger. And when I make the map 20x20 instead of 11x11, it begins to lag.
Also, for my project, I wouldn't move the map tile by tile, but by a quarter of a tile (or less) for chars which have multiple walking pics to them. This is why I'm asking for feasible methods on making this even faster before I begin, because if my idea won't make a significant difference, then I won't bother. (I'm trying to avoid DirectX for now).
Also, where's a good place to get chars like the ones you used for this example and others? Thanks for any input, and again, VERY nice code!!
BillSoo 08-08-2001, 03:55 PM Thanks for the kind words....
I don't really remember where I got the tilesets from...I did a web search for "tileset" and found something that looked usable. I think it is originally from Final Fantasy 7 so I would not use this in any kind of commercial product...
Anyway, I did another web search and found <a href="http://www.eagleeyerpg.com/95tiles.html"> THIS SITE </a> which seems to have a number of tilesets and character sets for download, some copyrighted, some not.
As for your memory DC question, I don't know if it would be faster or not. I don't know why it would be faster since the picturebox is invisible (ie. does not need to be updated). It will probably take up less memory since you don't have the overhead associated with a window, but faster? I don't know....
A method that *might* be faster than BitBlt is direct memory access. Basically you make a 2d array of bytes that correspond to pixels. You then map this array to the same block of memory that the picturebox uses. Thereafter, any changes to the array will result in changes to the picture. You can use copymem to copy to and from arrays....
This technique is around 3x faster than GetPixel/SetPixel so I suspect that it may be faster than BitBlt as well. But I don't know that for certain....
If you want to know more about this technique, I'll try to find some code....
....actually, now that I think of it, I have a program on PlanetSourceCode that uses this method. Do a search for "Kaleidescope" on PSC.
"I have a plan so cunning you could put a tail on it and call it a weasel!" - Edmund Blackadder
Computer_Guy 08-09-2001, 08:17 PM Okay, I understand how to initialize and run the two loops and start to make the world map in your form. The thing I don't understand is how you pick out the individual tiles in each tileset. I've looked at your code and I am still baffled, I also don't get how you make parts of the character transparent, could you please help me out with this?
BillSoo 08-10-2001, 02:54 AM I assume you mean this part of the code:
<pre>
ndx = Map(r, c)
x2 = (ndx Mod 8) * 32
y2 = (ndx \ 8) * 32
BitBlt picMap.hdc, x1, y1, 32, 32, picTiles.hdc, x2, y2, vbSrcCopy
</pre>
Let's take it line by line....
ndx = map(r,c)
This line retrieves the tile index that is located at the map coordinates r,c.. There are 256 tiles and I've given each one a unique index from 0 to 255.
x2 = (ndx mod 8) * 32
Having the index is nice, but we need to know the x,y coordinates of the top left corner of the tile on the tile bmp. Since there are 8 tiles per row, we can determine which column it is by ndx mod 8. This returns a number between 0 and 7 which corresponds with the column number. Since each tile is 32 pixels wide, multiplying the column number by 32 gives us the x coordinate of the left edge of the tile.
y2 = (ndx\8) * 32
In a similar manner, we can find the row number by dividing by 8. Note that we use the integer divide (\) rather than the real number divide (/). This prevents any rounding upwards so tiles 0 through 7 are in row 0, tiles 8 through 15 are in row 1 etc. Again, multiplying the row by 32 will give the pixel location of the top edge.
BitBlt picMap.hdc, x1, y1, 32, 32, picTiles.hdc, x2, y2, vbSrcCopy
BitBlt merely copies the 32x32 pixel tile from the tileset to the map.
"I have a plan so cunning you could put a tail on it and call it a weasel!" - Edmund Blackadder
JDYoder 08-10-2001, 11:20 AM FYI, I tried it and put all the DC's into memory, but like you suspected BillSoo, it made no difference. And I checked out your Kaleidescope entry on PSC -- very nice. If I get to the point where I really REALLY want to do a graphical RPG, I may look at that method sooner since it's lagging graphics that have deterred me from even attempting an RPG.
However, I'm beginning to think DirectX is the inevitable juggernaut out there that I need to face off against. Hopefully it's not as difficult as my intimidation of it. Of course, if I ever find a good 2D, map scrolling, DirectX engine out there, that would be ideal.
Spectre 08-10-2001, 02:16 PM If you're gonna do a 2D game, i suggest using DirectX7 and DDraw [or, you can wait for DX9 and see if they decide to reinclude DDraw :)] because DX8 nixed DDraw and you have to use D3d, which is a pain because textures have to be 2^n x 2^n... not good
anyhow, look into DDraw7 .. oh, and someone who posts often likes to give links to a DX help site... i found it VERY helpful, if only i can remember which site it is... i think Squirm is the one who posts the link..
bobthebuilder 08-12-2001, 09:31 AM i know a couple good ones... check my post directx game making.. i got a few reply with 2 good sites.. check em out.
-The Builder
I modified a couple lines in your Timer function to make the image not look like it's running in place:
<pre>
If flip Then
c = CharacterID * 64
Else
If Direction Then
c = CharacterID * 64 + 32
Else
c = CharacterID * 64
End If
End If
</pre>
"The face of a child can say it all, especially the mouth part of the face." - Jack Handy
Computer_Guy 10-02-2001, 07:39 PM What does the flip thing do? Is that the thing that makes the image switch? And is the whole +32 thing just so you can bitblt the picture?
BillSoo 10-02-2001, 08:22 PM The flip routine is to animate the charactor. It selects which bitmap to use. The +32 is because each character tile in the bitmap is 32 pixels square. Take a look at the project and look at the bitmaps to see what I mean.
"I have a plan so cunning you could put a tail on it and call it a weasel!" - Edmund Blackadder
Computer_Guy 10-03-2001, 05:28 PM That's what I had thought about the + 32 thing, but, what exactly is the CharacterID variable? What does it do? I don't have VB right now, so I can't really look on it.
BillSoo 10-05-2001, 12:42 AM The character bitmap actually has 10 characters on it. The characterID variable selects which character to use.
"I have a plan so cunning you could put a tail on it and call it a weasel!" - Edmund Blackadder
Squirm 02-16-2002, 05:49 PM Reattachment of this cool example :cool:
BillSoo 05-04-2002, 02:09 AM I've reworked this example to use smooth scrolling (ie. pixel by pixel rather than tile by tile). I've also changed it to use a fast DO-LOOP structure rather than a timer. The frame rate on my P500 was ~800 which was uncontrollable, so I had to add a delay loop to reduce the frame rate to 100.
|