Free Web Hosting Provider - Web Hosting - E-commerce - High Speed Internet - Free Web Page
Search the Web

vbProgramming | Tutorials | Games | RPG Programming I : Setting it up
    vbProgramming 
Tutorials -
RPG Programming Series:
Stage I. Setting It Up.
 
     

vbProgramming Home :: vbProgramming Forums :: Tutorials :: Contact :: Links 

 

 

This is the first tutorial in the RPG Programming Series

Intro
Welcome to the first tutorial in the RPG Programming Series. Instead of making one big tutorial for our RPG, we'll simply break it up into several stages to make things easier. Since this is the first stage, we won't worry about story design, level design..etc. For now, we'll focus mainly on getting our basic game to function. Since this series will be rather large, I ask that you please bear with me as I release these tutorials, it may take some time. Furthermore, since there will be massive amounts of code - any code previously explained in another tutorial will not be elaborated on here. Please be sure that you have learned and "mastered" the tutorials in Section I. Please remember to read tutorials in Section I and II, that's a prerequisite for this series.

Theory
Before we make this game, we need to plan it out. This section will focus on and give you ideas about how to approach various subjects.

First of all, when we make our game, we're obviously going to have levels. Let's say the character finishes level I, and he goes on to level II. Now wait, how will we redraw level 2? Do we do something like:

If Level1 Then
<Draw objects on Level1>
ElseIf Level2 Then
<Draw objects on Level2>
End If

No! That's preposterous. The best thing to do is change some variables around <-- yeah that sounded vague. Let me elaborate. We're going to learn a concept called Scripting, which we'll touch on later - but we'll pseudocode it out right now.

Instead of using the method above, what we can do is change the variables loaded. For example, there'll be a variable which will store the map name, when the new level loads we'll just feed in a different map name. We'll have to change the character's position in the world, so the map text file (see  Reading and Writing Text files) will also hold the character's default position.

Another way we can approach this is to have a textfile for each level (A level is different from a map). The Level text file will hold:
a) The path to the map text file
b) Default character location

When we load in a new level, we'll just say something like MainGameClass.CurrentLevel = "level2.txt". It will begin reading the file. It stores the path tot he map text file in a variable, and default character location into a variable. Then it will read the map text file, and thus a new array of tiles (see Tile Based Collision Detection) will be stored into some MainGameClass.Tiles(,) variable. Form1_Paint will still be rendering the tiles in MainGameClass.Tiles(,) ... but the only difference is, a new set of tiles will have been loaded. The character's position will be set to a new position (the value stored in Default Character Location). Woah, that took a while to explain.

In any case, if you don't understand - don't worry. It's just theory. Some guys learn better when you tell them what's coming first ;). Others like to see the code, and then they'll understand. So if you don't understand I'm guessing you'll be the latter people.

Anyways, I'm just giving you a heads up on what's coming. Now it's time to get started!

Getting the images
Please note: The images are not mine. My friend has a program called RPG Maker, and he gave me these images to use.

First things first, our RPG won't be very .. how should I say it... big on graphics. It's up to you to get some more images if you want :). For now, we'll makeshift with these 3 map images.
Create a new project called RPG Engine. Save these images into your bin folder in the tiles\basis folder.
(1.jpg)

(2.jpg)

(3.jpg)


Now for the character images:
-------------------------------------
There's an error on 8k.com which prevents you guys from downloading any zip files..etc, after searching through their
help support, I managed to find a 'quick fix'.
To download any of my files, visit this URL
http://www.nocache.vbprogramming.8k.com/files/alex.zip
then click alex.zip. If that doesn't work then you can always try the normal way:
http://www.vbprogramming.8k.com/files/alex.zip

--------------------------------------
Extract those images and save it in your bin folder under the Sprites\Alex folder.
Now you should have images in these folders:
bin\sprites\Alex\ (12 images in this folder)
bin\tiles\basis (3 images in this folder)

Take a quick look at the files and how they're arranged. Now let's get coding!

Setting up our clsSprite class
Remember, we've already covered most of this.
Instead of throwing all the sprite code into Form1, let's make it cleaner by adding a clsSprite class to store the position/frame/direction..etc of the sprite.

Create a new class called clsSprite, and add the following variables:

 'Stores the position (x,y) of the sprite. Sometimes these values will be decimals, so use PointF
Public Position As PointF
'Stores the folder in which the Sprite images are located in
Public Folder As String
'Stores the current frame (1 2 or 3) of the sprite
Public Frame As Integer

What we're going to do is have an array of images for each direction/frame. Declare the following constants:
 'Stores the number of Directions each sprite can have
Public Const NumberOfDirections As Integer = 4
'Stores the number of Frames each sprite can have
Public Const NumberOfFrames As Integer = 3

 Now declare the following bitmap:
 'Holds all the images for the sprite
Public SpriteImage(NumberOfDirections, NumberOfFrames) As Bitmap

Now wait, let's fix bad habits before we continue. We know that arrays begin with 0. So SpriteImage(4,3) really holds 5 * 4 = 20 images, not 4 * 3 = 12 images. The extra 8 images will hog up memory! So, simply change that line to:
Public SpriteImage(NumberOfDirections - 1, NumberOfFrames - 1) As Bitmap

Cool, simple enough so far. Now we have to store the direction. Rather than saying to ourselves, "0 is up, 1 is down"...etc, a better way is to use an Enum (short for Enumeration):

 'Stores the current direction of the sprite
Public Enum Dir
  Up = 0
  Down = 1
  Left = 2
  Right = 3
End Enum
'Create a copy for our sprite
Public Direction As Dir

What exactly is an enum? It's just another way to refer to a number. Instead of remember what direction corresponds to whatever number, it's much easier to use Enum. It's sort of like "Renaming" a number, so to speak. Let's say we push right. We'd simply say MySprite.Direction = Direction.Right instead of saying:
dim direction as integer and mysprite.direction = 3. It's a much easier way to do this. If you don't understand this, you'll figure out more as we go along.

Now, our New sub will load the sprite images into the array. In this game, all of our sprites will be stored in bin\Sprites\. Each individual sprite will be stored in bin\Sprites\MySpriteName\... For example, our Alex sprite is stored in bin\Sprites\Alex\. So, to find out where the sprites are, we just need the sprite name! So declare:
 Public SpriteName As String

Storing our images to the SpriteImage array
Let's keep moving. I think we're done with variables for now, let's create the New sub. This sub will store our images

 'Constructor.

Public Sub New(ByVal NameOfSprite As String)
  SpriteName = NameOfSprite
End Sub
Now wait, when we store our images, we can simply say <Don't type this in, just look at the following code>:
 'Sprites\Alex\right1.GIF
SpriteImage(Dir.Right, 0) = New Bitmap("Sprites\" & NameOfSprite & "\right1.GIF")
SpriteImage(Dir.Right, 1) = New Bitmap("Sprites\" & NameOfSprite & "\right2.GIF")
SpriteImage(Dir.Right, 2) = New Bitmap("Sprites\" & NameOfSprite & "\right3.GIF")

Yeah, we could say that and go through it for every direction, which would lead to 12 different directions. Instead of doing it this way, why not brush up our programming skills and doing it the harder (but shorter) way :D. First of all, I want you to notice how when the FrameNumber (2nd dimension element in SpriteImage) is 0, the actual image is right1. I .. erm .. messed up. I should have named my images right0.GIF, right1.GIF, right2.GIF ... that was my fault. So the image is really the FrameNumber + 1.

Ok, now here's how to do this, here's some pseudocode:
-Loop through every direction and frame number
-Set each bitmap.

Heh, doesn't sound hard as it looks! Here's the "Loop through every direction and frame number" part.
Dim CurrentDirection As Integer
Dim CurrentFrame As Integer

For CurrentDirection = 0 To NumberOfDirections - 1
   For CurrentFrame = 0 To NumberOfFrames - 1

  Next
Next


The next part is a bit tricky, normally you'd expect to do something like (this is inside the loop remember) :
 SpriteImage(CurrentDirection, CurrentFrame) = New Bitmap("Sprites\" & NameOfSprite & "\" & CurrentDirection & CurrentFrame + 1 & ".GIF")
But if you really look at it, that translates to [Pretending CurrentDirection and CurrentFrame are both 0]:
Sprites\alex\01.GIF

That's not what we want, we want it to say:
Sprites\alex\up1.GIF

So our problem right now is "Translating" our CurrentDirection into text from numbers. Pretty simple - Create a function called ReturnDirection:

'ReturnDirection returns a String from an Integer value provided by the argument
Public Function ReturnDirection(ByVal CurrentDirection As Integer) As String
  If CurrentDirection = Dir.Up Then Return "up"
  If CurrentDirection = Dir.Down Then Return "down"
  If CurrentDirection = Dir.Left Then Return "left"
  If CurrentDirection = Dir.Right Then Return "right"
End Function

To simplify it, take the first statement for example.. it translates into:  If CurrentDirection = 0 Then Return "up" . Understand? Now let's fix our line in the New sub:
SpriteImage(CurrentDirection, CurrentFrame) = New Bitmap("Sprites\" & NameOfSprite & "\" & ReturnDirection(CurrentDirection) & CurrentFrame + 1 & ".GIF")
 
Just a quick refresher, now our New sub should look like this:
Public Sub New(ByVal NameOfSprite As String)
   'Store the SpriteName (the sprites are stored in bin\Sprites\SpriteName)
    SpriteName = NameOfSprite
    Dim CurrentDirection As Integer
    Dim CurrentFrame As Integer
    'Loop through every Direction
    For CurrentDirection = 0 To NumberOfDirections - 1
           For CurrentFrame = 0 To NumberOfFrames - 1
             
     'Set each bitmap (all 12)
                   SpriteImage(CurrentDirection, CurrentFrame) = New Bitmap("Sprites\" & NameOfSprite & "\" & ReturnDirection(CurrentDirection) & CurrentFrame +   1 & ".GIF")
'These aren't 2 different lines, the page isn't wide enough to fit the entire line.
          Next
     Next
End Sub

Heh, I think I just took something that was simple (and long) and made it a bit more complicated (and shorter). Hey at least it gave you a quick refresher on Return values!

Now let's test out our class! Go back to form1 and in your globals:
'The alex sprite has the name of.. alex, and is stored in the \Sprites\alex folder
Dim alex As New clsSprite("alex")

Now go to Paint:

 e.Graphics.DrawImage(alex.SpriteImage(alex.Direction, alex.Frame), alex.Position)

As you can see, we've effectively minimized the amount of code in Form1 because we used classes. Run the app, you'll see that we forgot to set transparency. So go back to clsSprite, in the New sub
right after SpriteImage(CurrentDirection, CurrentFrame) = New Bitmap(etc......):
SpriteImage(CurrentDirection, CurrentFrame).MakeTransparent(Color.Lime)

There now it's transparent!

Movement
Well, again this is pretty easy since there's been several tutorials on animation. We're going to do Tile Based animation. The majority of our code will be in clsSprite. Since we'll use OOP Practices, you'll see a lot less code in Form1 (thus, avoiding clutter). First, in the clsSprite globals:
Dim InMotion as Boolean

This variable will check if the sprite is currently InMotion. We don't want him to change direction when he's already moving, so we use this as a "Safeguard" so that he can't move in mid-motion.

Now I'm going to do something which may seem absolutely random to you. However, in the long run, it will make sense. Create a new class called GameClass. And in GameClass:

 'The form in which we will draw to.
Public Shared GameForm As Form

Public Sub New(ByVal frm As Form)
'Set the form that we'll draw to
GameForm = frm
End Sub

 Now go back to form1, and simply type in (in Globals)
 Dim Game As New GameClass(Me)

The reason why we're doing this is to store the form in which we're going to draw to in the GameClass so that it can be used by other classes (which is the reason why it is Shared). If you remember from my other tutorials, Shared means that any class can access that object without instantiating that class.. quick example:
If you had classA and classB, and you wanted to access GameForm, you'd simply refer to it as: GameClass.GameForm (you can do the same with any class).

Now, remember we've already done this before, so the following code (hopefully) shouldn't  be a big surprise to you. Take a look at the code to move the sprite up (in clsSprite)

Public Sub MoveUp()
  
'If the sprite is currently not moving Then move him up
    If Not InMotion Then
     
'Now he's in motion.
      InMotion = True
 
    'Loop Variable
      Dim x As Single
  
   'Set his direction
      Direction = Dir.Up

 
   'Loop 30 times.
      For x = 1 To 30
          '
Move him one unit up, 30 times.
           Position.Y -= 1
         
 'Let your application do other events (ex: Check for keypress and refresh the form)
          
'in this loop.
           Application.DoEvents()
         
 'Refresh the form
           GameClass.GameForm.Invalidate()
      Next
   
  'Now he's done moving.
      InMotion = False
  End If
End Sub

Wow, that code took quite a while to format :). Anyways, I'm sure you understand this, it simply moves the character one unit, 30 times (meaning 30 units). The reason I did the whole GameClass thing was to be able to refresh the Form from the loop itself.

We're going to make several changes to these Move subs, so I won't paste all 4 right now, I'll paste them when we're done making the changes. For now, assume we've created all 4 (or create them yourself if you wish), and go to form1_keydown. This shouldn't come to a surprise to you at all (besides the fact that you probably don't have a MoveDown, MoveLeft, and MoveRight subs yet - if you don't, just comment those lines out):
 'Select case is just another way of writing a bunch of If statements. Pretty self explanatory
Select Case e.KeyCode
   Case Keys.Up
      alex.MoveUp()
   Case Keys.Down
      alex.MoveDown()
   Case Keys.Left
      alex.MoveLeft()
   Case Keys.Right
      alex.MoveRight()
End Select
'Display his position
Me.Text = "Current Position: " & alex.Position.ToString()

Oh and, go to form1_load, I hope you remember seeing these:
Me.SetStyle(ControlStyles.DoubleBuffer, True)
Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
Me.SetStyle(ControlStyles.UserPaint, True)

The last one might come as a surprise to you. Earlier, I thought that DoubleBuffer and AllPaintinginWMPaint is enough to reduce the flicker, but some information (given by a fellow vbProgrammer) indicated to me that you need UserPaint as well.

Now run the project (heh, I mean those of you who did the MoveDown MoveLeft and MoveRight subs :)). I'm sure most of you don't have it, but don't worry about that, I'll give you the code for those subs soon. By the way, since you only have a MoveUp sub, why not just change the guy's default position to 120,0 to test out his movement. To accomplish this, just change the line to:
 Public Position As PointF = new PointF(0,60)

Controlled Movement

First thing you'll notice is (especially if you have a good computer): HE MOVES TOO FAST!  One thing I should tell you right from the start, GDI+ is slow. The moment you start adding in objects, you won't need to slow down the guy, he'll go really slow! But anyways, we need to adapt our game to newer/older computers. First of all, (we'll implement this later), we'll do a check to see how many frames per second you get. And based on that, we'll figure out how fast the character must move.

Now, listen up :P. We need to plan ahead. So before moving on to animation, let's fix problems before they pop up again! First, let's experiment with incrementing his position by 0.1 each time:

See the 30 in the loop (For x = 1 to 30)? Change that to 300. See the Position.Y -= 1? Change the 1 to a 0.1 & now you're all set. Try moving the character

 Now you'll see that he moves normally (or way too slow for some of you guys with slower computers, or even too fast for the guys with fast computers!). Don't worry about the differences yet. Take a look at your position being displayed when you move up. When you move up once, your position should be (30,0), but for some odd reason it shows up as 30.0000000000042 or something like that. It's really strange, I have no idea why .NET messed up my number like that. It probably doesn't like my processor (hyperthreaded). These [very] tiny changes can certainly screw up your number when it comes to collision detection. To counter that problem, I simply rounded the number to 0 decimal places (aka, whole numbers). So in the MoveUp sub, before InMotion = False, I typed in:
 Position.Y = Math.Round(Position.Y, 0)

As I said before, '0.1' is NOT going to work once we start adding in objects. You'll think he's going slow for some reason :). What we need to do is figure out a formula to relate the Loop Variable (right now it's 300) to the Increment variable (it's 0.1 right now).

Well it's pretty simple, first: Change the 300 back to 30 and the 0.1 back to 1. When we changed the 30 to a 300, we multiplied by 10. When we changed the 1 to a 0.1, notice how we multiply and divide by the same number? That number will be our Multiplier:
'This number will be the multiplier and will be adjusted according to the computer's speed. The higher it is, the slower your game will be. The default value is 1.
Public Multiplier as Single = 1


Now, go to the MoveUp sub:
For x = 1 To 30 * Multiplier
     Position.Y -= 1 / Multiplier

If you don't understand this, go back to the '.1' example. Our multiplier was 10. For now, play around with the default multiplier value until you get what's best for your computer. Hint: Set your form's WindowStyle property to Maximized because we're going to do a FullScreen game. The fact that your form is maximized slows down your character considerably :). Why? Because your form is busy drawing the gray for the background color (when you do Me.Invalidate, the form redraws it's background color as well!) - and since you've maximized it, there's a lot more background to draw.

I've found a little trick around this: Normally, to select an Event, you'd go to Base Class Events (or Form Events for those of you with VS.NET 2003) and select your event from the drop down menu. This time, go to (Overrides) instead of Base Class Events and select OnPaintBackground. It'll (obviously) automatically create a sub for you - leave it empty. We haven't learned about On events yet (ex OnPaintBackground). What this event USED to do (before you overrided it) was paint the form background color. Now that you've overridden it, you're basically telling the form: "Hey, I'll give you the code for painting the background, not you". And since you're leaving it blank, the form background shows up as black (no, it is not filling the color as black, it's just... the .. blank color).

Therefore you don't have a background anymore, and your application should speed up (a bit). The other thing that's making it slow is your form size, but don't worry about that.

Anyways, back to multiplier. Set the multipler to what you want. By the way, setting the multiplier to a very small number isn't going to help. For example if you set the multiplier to 1/100:
For x = 1 to 30 * 1/100........... aka For x = 1 to .3 ..... that woudn't work out :). 1/30 won't help either cuz he'd jump from 1 tile to the next. So please, don't go crazy with multiplier values. I'd say 1/ 5 is the absolute smallest you can go (using 1/5 will only move him 6 times!!).  Ye who hath the exalted AlienWare - don't brag :D, your computer wont take a multiplier greater than like... 2 when the game's done ;).

Also remember, later we're going to implement an FPS (Frames per Second) counter. We'll adjust the multiplier according to the FPS.

Anyways, for now, on FullScreen, my computer likes a multiplier of 1 and 2. He moves kind of fast, but you won't be saying that when we render the map.

Animation
Let's get to animation! (This is the last thing I'll speak about before giving you the MoveDown MoveLeft and MoveRight subs).

Animation is rather easy since we've already set up most of it! We know that all we have to do is increment the frames and be sure that the frames don't go higher than 2 and less than 0. So,  let's create an Animate sub!

 Public Sub Animate()
   'Make him move
   Frame += 1
   'Don't let it go too high!
   If Frame = 3 Then Frame = 0
End Sub


Now go back to your move up sub. Right after you change Position.Y, type in:
 Me.Animate()

Me.Animate() is the same as typing in Animate(). So anyways, run the project and the guy should animate.

Here are the rest of your subs (I've reduced the font, the code takes up a lot of space).

*****UNFORMATTED CODE (Paste into .NET to see the formatting)*****
 Public Sub MoveUp()
'If the sprite is currently not moving Then move him up
If Not InMotion Then
'Now he's in motion.
InMotion = True
'Loop Variable
Dim x As Single
'Set his direction
Direction = Dir.Up
'Loop 30 times controlled by the multiplier
For x = 1 To 30 * Multiplier
'Move him one unit up, 30 times controlled by the multiplier
Position.Y -= 1 / Multiplier
'Animate the sprite!
Me.Animate()
'Let your application do other events (ex: Check for keypress and refresh the form)
'in this loop.
Application.DoEvents()
'Refresh the form
GameClass.GameForm.Invalidate()
Next
'Round his decimal place
Position.Y = Math.Round(Position.Y, 0)
'Now he's done moving.
InMotion = False
End If
End Sub

Public Sub MoveDown()
'If the sprite is currently not moving Then move him up
If Not InMotion Then
'Now he's in motion.
InMotion = True
'Loop Variable
Dim x As Single
'Set his direction
Direction = Dir.Down
'Loop 30 times controlled by the multiplier
For x = 1 To 30 * Multiplier
'Move him one unit up, 30 times controlled by the multiplier
Position.Y += 1 / Multiplier
'Animate the sprite!
Me.Animate()
'Let your application do other events (ex: Check for keypress and refresh the form)
'in this loop.
Application.DoEvents()
'Refresh the form
GameClass.GameForm.Invalidate()
Next
'Round his decimal place
Position.Y = Math.Round(Position.Y, 0)
'Now he's done moving.
InMotion = False
End If
End Sub

Public Sub MoveLeft()
'If the sprite is currently not moving Then move him up
If Not InMotion Then
'Now he's in motion.
InMotion = True
'Loop Variable
Dim x As Single
'Set his direction
Direction = Dir.Left
'Loop 30 times controlled by the multiplier
For x = 1 To 30 * Multiplier
'Move him one unit up, 30 times controlled by the multiplier
Position.X -= 1 / Multiplier
'Animate the sprite!
Me.Animate()
'Let your application do other events (ex: Check for keypress and refresh the form)
'in this loop.
Application.DoEvents()
'Refresh the form
GameClass.GameForm.Invalidate()
Next
'Round his decimal place
Position.X = Math.Round(Position.X, 0)
'Now he's done moving.
InMotion = False
End If
End Sub

Public Sub MoveRight()
'If the sprite is currently not moving Then move him up
If Not InMotion Then
'Now he's in motion.
InMotion = True
'Loop Variable
Dim x As Single
'Set his direction
Direction = Dir.Right
'Loop 30 times controlled by the multiplier
For x = 1 To 30 * Multiplier
'Move him one unit up, 30 times controlled by the multiplier
Position.X += 1 / Multiplier
'Animate the sprite!
Me.Animate()
'Let your application do other events (ex: Check for keypress and refresh the form)
'in this loop.
Application.DoEvents()
'Refresh the form
GameClass.GameForm.Invalidate()
Next
'Round his decimal place
Position.X = Math.Round(Position.X, 0)
'Now he's done moving.
InMotion = False
End If
End Sub


Wow, we're done with movement and animation.  This tutorial is getting quite long... and this part almost took me 2 hours to type lol. Anyways, I'll stop rambling. I was thinking of doing Loading the Map and Collision Detection in another tutorial, but nah - we'll do Loading the map now and then Collision in the next one.

Loading the map
Please be sure you've read Reading and Writing text files. In clsSprite:

Public ReadOnly Property CurrentTile() As Point
   Get
      Return New Point(Position.X / 30, Position.Y / 30)
   End Get
End Property


We've done this, pretty self explanatory. The function will return the tile which the character is on.

----------------------
Now, we need to make our application more "OOP", even though it already is. Our clsSprite class should be able to access our clsMap class (which we'll create) and vice versa.  What we're going to do is declare the clsSprite inside GameClass instead of Form1 so that clsMap can access it:
 Public Shared alex As New clsSprite("alex")

Remember, shared means all the other classes can access it. Now, go back to form1 and delete the line that declares alex as a clsSprite.  Obviously a bunch of errors will come up:
1) Hit control + F. 
2) Click the Replace button
3) In the Find textbox, type in alex
4) In the Replace textbox, type in Game.alex
5) Be sure that the option "Search In: Current Document" is checked instead of "Search: All Open Documents"
5) Hit 'Replace All
'

Not that hard, we're just putting alex in gameclass so that clsMap can access it.
----------------------

Here's the map:
22,16
2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,1,1,1,1,1
3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
1,1,1,1,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,2
1,1,1,1,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,2
1,1,1,1,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2
1,1,1,1,2,1,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,2
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
3,1,1,1,1,1,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
1,1,1,1,2,1,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3

Copy and paste everything in black into notepad and save it under \bin\maps\lightworld.map

Just to give you a refresher from the Reading and Writing text files tutorial, the first 2 numbers are the width and the height of the map. The rest of the numbers correspond to each bitmap (ex: 2 corresponds to "2.jpg" and so on and so forth).

Here's clsMap
*****UNFORMATTED CODE (Paste into .NET to see the formatting)*****
Public Class clsMap
'The thing which will read our map
Dim SR As System.IO.StreamReader
'Size of the map
Public Width As Integer
Public Height As Integer
'Will store all the tiles
Public Tiles(,) As Bitmap

Public Sub New(ByVal MapName As String, ByVal tileset As String)
ReadMap(MapName, tileset)
End Sub
Public Sub ReadMap(ByVal MapName As String, ByVal tileset As String)
'Read a certain map file
SR = New System.IO.StreamReader("maps\" & MapName)

Dim ln As String
'This will store the width/height of the map, seperated by commas.
ln = SR.ReadLine()
'This will store the width and the height individually
Dim str() As String
'Split ln from it's commas
str = ln.Split(",")
'Store the width and the height
Width = str(0) - 1
Height = str(1) - 1

'Now that we know the height and the width:
'Redim Tiles according to height and the width of the map
ReDim Tiles(Width, Height)
'Stores the current row/column being read
Dim CurrentRow As Integer
Dim CurrentColumn As Integer

Dim Line() As String

For CurrentRow = 0 To Height
'Read each Line
ln = SR.ReadLine()
Line = ln.Split(",")
For CurrentColumn = 0 To Width
'Read each character (Go across the file)
Tiles(CurrentColumn, CurrentRow) = New Bitmap("tiles\" & tileset & "\" & Line(CurrentColumn) & ".jpg")
Next
Next
End Sub
End Class


Back in GameClass:
 'Declare the map. The tiles for this map are found in the basis folder, and the map
'file is found in lightworld.map

Public Shared map As New clsMap("lightworld.map", "basis")

In form1:
 Dim x, y As Integer

In form1.Paint (do this before you render the character):
For x = 0 To Game.map.Width
   For y = 0 To Game.map.Height
      e.Graphics.DrawImage(Game.map.Tiles(x, y), x * 30, y * 30)
   Next
Next

Run it, you should see the map being rendered (woohoo). Yeah... that's pretty much it for this tutorial.
Next up: Collision Detection, Scrolling, and Changing Screen Resolution (something new... yay!)

Have a nice day!

 

The Source Code for this tutorial is located here:

You can also locate this by logging in to vbProgramming Forums and going to:
Tutorials > Tutorial Source Code > Source Code