Lesson 26.1 Displaying a World Map

Several people have asked about adding graphics to this game. In this lesson, I’ll show how to create a World Map screen, and display images for the locations.

This is different from the way I display images in a WPF program (like in the “Build a C#/WPF RPG” lessons). WPF is a little easier.

I’m working on a second lesson for these graphics, to show how to only show the images for the locations the player has visited.

 

 

Step 1: Add the location image files to the SuperAdventure project.

Download the zip file below, which has image files to use for the locations built in to the default game. After you download the zip file, uncompress it and remember where you store the uncompressed image files.

These are all 125 x 125 pixel PNG images, with transparent backgrounds. In this lesson, we will display them in 75 x 75 pixel squares, and re-size the images to fit.

 

DOWNLOAD IMAGE FILES HERE

 

In the SuperAdventure project, create a new “Images” folder.

Add all the images from the zip file by right-clicking on the Images folder and selecting Add -> Existing Item…

After the images are added to the project, right-click on each of them, and select “Properties”.

Set “Build Action” to “Embedded Resource”. This will include the images as part of the assembly file (program), when Visual Studio builds the solution. Also set “Copy to Output Directory” to “Do not copy”. You don’t need to copy the files, because they will be embedded in the program.

 

 

Step 2: Create a new Windows Form in the SuperAdventure project, named WorldMap.

Right now, you don’t need to do anything with it. We will do more with it in Step 4.

 

 

Step 3: Modify SuperAdventure.cs, in Design mode.

Add a new button to display the WorldMap window. I created a new button with these properties:

Name: btnMap

Text: Map

Location: 494, 457

Size: 75, 23

This can put this new button in the middle of the four movement buttons. You might need to adjust the locations of the existing movement buttons, to make the buttons look nice.

 

 

Double-click on the new button, so Visual Studio will create the eventhandler in SuperAdventure.Designer.cs and the new btnMap_Click function in SuperAdventure.cs.

 

Edit SuperAdventure.cs, and add these lines to the btnMap_Click function:

 

Now, you can test the program by running it and clicking on the “Map” button. The new WorldMap window should pop up.

 

 

Step 4: Edit SuperAdventure\WorldMap.cs, in Design mode

Now, we will add the images to the map window.

This will be a simple way to display the location images. If you build a bigger world, you might want to change this to only show the locations immediately surrounding the player’s current location.

If you don’t want to manually create all the controls to hold the images, the source code on Gist and Dropbox include the Designer.cs file – which is where all the control information is located. So, you can just copy those over your files, if you use the same name (WorldMap) for your new form.

 

First, right-click on the WorldMap window and select “Properties”. Set these values:

Text: World Map

Size: 501, 361

MaximizeBox: False

MinimizeBox: False

SizeGripStyle: Hide

 

We’re going to display the images with PictureBox controls. This control type is in the Toolbox, under the Common Controls.

Left-click on the PictureBox and drag it to the upper-left corner of the WorldMap window. After positioning the PictureBox control, right-click on it and set these properties:

BorderStyle: FixedSingle

SizeMode: StretchImage

(name): pic_0_0

Location: 0, 1

Size: 75, 75

This will draw a border around the location’s image, resize the image to fit the size of the picture box control, and set its size.

 

Next, you need to add 23 more of these PictureBox controls. We will have four rows and five columns, in total. You want the screen to look like the image below:

 

 

You can do this a little faster by placing the first PictureBox, left-clicking on it once, and using Ctrl-C to put it in your clipboard. Then, you can press Ctrl-V on the WorldMap window, to paste in a copy of the PictureBox. Then, drag the new PictureBox to the correct location.

After you’ve placed all 24 PictureBox controls, you’ll need to right-click on each one and give it its name. They’ll all start with “pic” and be followed with an underscore and the row number (starting at the top, with 0 as the first row) and another underscore with the column number (starting at the left, with 0).

When you’re done, they should be named like this:

pic_0_0, pic_0_1, pic_0_2, pic_0_3, pic_0_4, pic_0_5

pic_1_0, pic_1_1, pic_1_2, pic_1_3, pic_1_4, pic_1_5

pic_2_0, pic_2_1, pic_2_2, pic_2_3, pic_2_4, pic_2_5

pic_3_0, pic_3_1, pic_3_2, pic_3_3, pic_3_4, pic_3_5

 

Step 5: Edit SuperAdventure\WorldMap.cs (the code-behind)

In Solution Explorer, right-click on WorldMap.cs, and select “View Code”.

In this form, we are going to find the image inside the assembly (the program), pull it out of the assembly, format it as a picture, and add the picture to the PictureBox control.

To do all that, we need some additional “using” statements at the top.

  • Drawing is for the Bitmap class, which builds the image to display
  • IO is to read the bytes of the embedded images, from the assembly
  • Reflection is to let us know what assembly we are using

 

On line 10, we have a new Assembly variable. The assembly is the EXE or DLL that is built when you have Visual Studio built the solution. We set the variable to Assembly.GetExecutingAssembly(), to get the EXE that is running – which is also where the image files are embedded.

Inside the constructor, we call our SetImage function, passing in the PictureBox control we want to fill, and the name of the image file to add to the PictureBox. For the image name, we aren’t including the .png extension, because we do that inside SetImage().

The SetImage function creates a stream, to read bytes from _thisAssembly (the variable we created to point to the running program).

The stream gets the bytes by looking for the resource with the name that matches the string we build on line 30. This value needs to follow the structure of your project’s namespace. So, if you didn’t name your folder “Images” in step 1, you will need to change “.Images.” to the name you used.

If the resource exists, resourceSteam will not be null. So, we takes the bytes from resourceStream and build a new Bitmap object with them, then put that Bitmap object in the PictureBox’s Image property (on line 35).

This is how we get the bytes for the image out of the program and put them on the screen.

It’s a bit of work. But, if you remember to make the image an embedded resource, and use code like the SetImage function, you can add images to to any Windows Form.

 

WorldMap.cs

 

Test that this works by running the program and clicking on the “Map” button. You should see the location images in the World Map window.

 

 

Next Lesson

I think that enough for this lesson. In Lesson 26.2 (which I’m starting work on now), I’ll show you how to only show locations that the player has visited. The other locations will be covered with a “fog” image.

 

Source code for this lesson

NOTE: Because the new WorldMap Form has many PictureBox controls, I’ve included the source code for the WorldMap.Designer.cs file.

Get it from GitHub: https://gist.github.com/ScottLilly/460c51d8b4b637d27b7d1e5f456e129b

Or Dropbox: https://www.dropbox.com/sh/cu6wrr9yd65qdab/AAAetrFP-52FtoIrWbiOJsRNa?dl=0

 

Previous lesson: Lesson 25.1 – Select a random monster at a location

Next lesson: Lesson 26.2 – Hiding Unvisited Locations on the World Map

All lessons: Learn C# by Building a Simple RPG Index

2 thoughts on “Lesson 26.1 Displaying a World Map

  1. Hi!

    I think it would be a little more elegant to use the LOCATION_ID constants from the world class, than using the integer numbers directly here:

    SetImage(pic_0_2, player.LocationsVisited.Contains(World.LOCATION_ID_ALCHEMISTS_GARDEN) ? “HerbalistsGarden” : “FogLocation”);
    SetImage(pic_1_2, player.LocationsVisited.Contains(World.LOCATION_ID_ALCHEMIST_HUT) ? “HerbalistsHut” : “FogLocation”);
    SetImage(pic_2_0, player.LocationsVisited.Contains(World.LOCATION_ID_FARM_FIELD) ? “FarmFields” : “FogLocation”);
    SetImage(pic_2_1, player.LocationsVisited.Contains(World.LOCATION_ID_FARMHOUSE) ? “Farmhouse” : “FogLocation”);
    SetImage(pic_2_2, player.LocationsVisited.Contains(World.LOCATION_ID_TOWN_SQUARE) ? “TownSquare” : “FogLocation”);
    SetImage(pic_2_3, player.LocationsVisited.Contains(World.LOCATION_ID_GUARD_POST) ? “TownGate” : “FogLocation”);
    SetImage(pic_2_4, player.LocationsVisited.Contains(World.LOCATION_ID_BRIDGE) ? “Bridge” : “FogLocation”);
    SetImage(pic_2_5, player.LocationsVisited.Contains(World.LOCATION_ID_SPIDER_FIELD) ? “SpiderForest” : “FogLocation”);
    SetImage(pic_3_2, player.LocationsVisited.Contains(World.LOCATION_ID_HOME) ? “Home” : “FogLocation”);

Leave a Reply

Your email address will not be published. Required fields are marked *