Part 1
First of all, we'll need a form with a tile selection area and a map editing area. Both should be able to scroll freely. The easiest way to do this is by having two AutoScroll-enabled
Panels containing two AutoSizing PictureBoxes. Place the two
Panels on a new Form, enable AutoScroll for both and position them nicely. Then, add a PictureBox in each, in the top-left corner and set to AutoSize. The tileset
panel should be as wi
de as the tileset (128px in this example) plus some margin for the scroll bar but frankly, we can afford to play it loose for now.
Interjection: I showed the draft for this tutorial to Cearn and he insists on using constants for tile sizes. I've
deci
ded to compromise -- this tutorial will use size constants, but I'm not updating the final source co
de archive. So whenever I say "128 pixels wi
de", that should be read as "eight times the tile size", id est "8 * 16".

der/get.php?id=1467>
Now that the UI is in place, we can go over the co
de. Now, I like to use backbuffers, so we'll go with that.
Define two Bitmap and two Graphics objects.
[co
de]
public partial class Tutorial : Form
{
-> private const int TileSize = 16;
-> Bitmap TilesetBackbuffer, MapBackbuffer;
-> Graphics TilesetGraphics, MapGraphics;
[/co
de]
Next up is loading and displaying the tileset. We'll go with a
der/get.php?id=1468>slightly cut Advance Wars tileset, but anything goes, really. In this example, as announced before, the tileset should be 128px wi
de, most any height. This maps to eight tiles a row. There's some math involved here that we'll get to later. Also note that you can't pull a Graphics object from in
dexed Bitmaps, so don't just try that.
[co
de]
public Tutorial()
{
InitializeCom
ponent();
-> TilesetBackbuffer = new Bitmap("tileset.png");
-> TilesetGraphics = Graphics.FromImage(TilesetBackbuffer);
-> tilesetPictureBox.Image = TilesetBackbuffer;
}
[/co
de]
Now, to select a tile from the tileset, we must remember which tile in
dex was selected. Add a CurrentTile variable to do this, and add the following event handler:
[co
de]
Graphics TilesetGraphics, MapGraphics;
-> int CurrentTile;
[/co
de]
[co
de]
private void tilesetPictureBox_MouseClick(object sen
der, MouseEventArgs e)
{
// Note: eight tiles per row in the tileset.
CurrentTile = ((e.Y / TileSize) * 8) + (e.X / TileSize);
this.Text = "CurrentTile: " + CurrentTile.ToString();
}
[/co
de]
I hear you ask, "Kawa, when the
hell are we getting to the mapping part?". Well, how 'bout right now?
First of all, we'll need something to store the current map in. For readability's sake, we'll go with a 2D int array.
[co
de]
int CurrentTile;
-> int[,] Map;
-> int MapWidth, MapHeight;
tilesetPictureBox.Image = TilesetBackbuffer;
-> MapWidth = 10;
-> MapHeight = 10;
-> Map = new int[MapWidth, MapHeight];
-> MapBackbuffer = new Bitmap(MapWidth * TileSize, MapHeight * TileSize);
-> MapGraphics = Graphics.FromImage(MapBackbuffer);
-> mapPictureBox.Image = MapBackbuffer;
[/co
de]
Next, we'll need something to draw the full map. This only has to happen un
der few circumstances, such as on load. For any subsequent change, drawing single tiles will suffice. We'll tackle both these tasks in one go.
[co
de]
private void DrawTile(int x, int y, int tileNo)
{
Rectangle sourceRect = new Rectangle((tileNo % 8) * TileSize, (tileNo / 8) * TileSize, TileSize, TileSize);
Rectangle
destRect = new Rectangle(x * TileSize, y * TileSize, TileSize, TileSize);
MapGraphics.DrawImage(TilesetBackbuffer,
destRect, sourceRect, GraphicsUnit.Pixel);
}
private void DrawFullMap()
{
for (int x = 0; x < MapWidth; x++)
for (int y = 0; y < MapHeight; y++)
DrawTile(x, y, Map[x, y]);
}
[/co
de]
[co
de]
mapPictureBox.Image = MapBackbuffer;
-> DrawFullMap();
[/co
de]
You should have a black map now, since the Map array
defaults to all zero, which is a black tile. So let's get to the fun part!
[co
de]
private void mapPictureBox_MouseMove(object sen
der, MouseEventArgs e)
{
int x = e.X / TileSize;
int y = e.Y / TileSize;
if (x < 0 || x >= MapWidth || y < 0 || y >= MapHeight)
return;
if (e.Button == MouseButtons.Left)
{
Map[x, y] = CurrentTile;
DrawTile(x, y, CurrentTile);
mapPictureBox.Refresh();
}
}
[/co
de]
Hook this up to both the MouseMove
and MouseDown events for mapPictureBox. Why both? Because if you only hook it to MouseMove, you have to jiggle the cursor every time you start drawing and if you only hook it to MouseDown, you can't do strokes.
That's it. You now have a simple map editing system going. Next time, we'll tackle loading and saving, probably followed by loading the assets from ROM.

der/get.php?id=1466>
der/get.php?id=1469>Download the finished app for this part____________________
Wife make lunch - Shampoo
Opera - give it a spin
Spare some of your free time?
<GreyMaria> I walked around the Lake so many goddamn times that my sex drive was brutally murdered
Kawa rocks
— byuu