C# winform map editor

Started by
9 comments, last by laztrezort 12 years, 8 months ago
Hi, for my XNA game i want a map editor. I already started on one within XNA i can manage but it's getting very tedious so i figured out how to use the intermediate serializer in a windows form application to make a nice user friendly editor.

I managed to create a imagebox array and when the user chooses to make a 20 by 20 map it loads fine. But if the user chooses a 100 by 100 map size it crashes (error creating window handler). I know it's 10K image boxes but i have seen map editors that allow 100*100+ maps. So i'm wondering if there is a better way of displaying such a tile map. I figured most maps will be 50*50 except the world map, that one will be huge.

Obviously those tiles should be editable not fixed. Eventually i want to be able to select multiple tiles and edit the texture, collision, area level etc.

I also want to add that i'm not using the XNA winform approach on app hub so i have no acces to the spritebatch. The only thing i use from XNA is the:
Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate;

and it serializes perfectly fine with this and a xna data library.

XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;

using (XmlWriter writer = XmlWriter.Create("f:/temp/testmap.xml", settings))
{
IntermediateSerializer.Serialize(writer, mapData, null);
}


Thanks,
Advertisement
Oh yeah, creating 10,000 controls is patently not a good idea. :)

Your easiest and quickest fix is to only allocate the visible images.
So how would i do that? I started off searching how to display images, i only came acros the picturebox method.
You'll want one PictureBox or Panel (PictureBox recommended), and you'll want to create your own Paint event handler which draws all tiles, pretty much like you do in XNA.
Truth of the matter is, Pictureboxes are probably not what you actually want either, but should work in a pinch.

As to actually answer, without seeing your code I really cant be too elaborate, but the gist is:

Lets say your world is a 1000x1000 2D array of 32x32pixel images and your editor window is 1024x768, that means you can visibly show 32x24 tiles at once.

So essentially you have a window into your world and only load in the tiles you need as you need them.
For example, lets say you want to be dead center in your world, you would load in the tiles at the position worldArray[ 500 - (32/2)][500 - (24/2)], then read in all the tiles that are currently visible into your picture boxes. If you pan the world around, you change the indexes into the worldArray when loading in the newly visible tiles.

Truth of the matter is, Pictureboxes are probably not what you actually want either, but should work in a pinch.

As to actually answer, without seeing your code I really cant be too elaborate, but the gist is:

Lets say your world is a 1000x1000 2D array of 32x32pixel images and your editor window is 1024x768, that means you can visibly show 32x24 tiles at once.

So essentially you have a window into your world and only load in the tiles you need as you need them.
For example, lets say you want to be dead center in your world, you would load in the tiles at the position worldArray[ 500 - (32/2)][500 - (24/2)], then read in all the tiles that are currently visible into your picture boxes. If you pan the world around, you change the indexes into the worldArray when loading in the newly visible tiles.


I have added that functionality in the game itself. i figured that would not be needed for a map editor since it only needs to load the pictures once and refresh the picture only when it is being changed.

At the moment i don't have much code, just the form to create a new map with input fields for the user. That is being put into a instance of my helper libarary with all the data needed for the map and passed on to the map editor form where the map should be drawn....

There i am making all the arrays that are needed, i'm using single dimension arrays because i know what the size of my map is so i know the location of tile[4584].



using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Helper_library;

namespace WinFormMapEditor
{
public partial class mapEditor : Form
{
public mapEditor(MapData mapData)
{
InitializeComponent();

PictureBox pb; //<-- need not need?

mapData.tiles = new TileData[mapData.mapWidth * mapData.mapHeight];

int count = 0;
for (int y = 0; y < mapData.mapHeight; y++)
{
for (int x = 0; x < mapData.mapWidth; x++)
{
TileData newTile = new TileData();
newTile.TextureName = "dirt";
newTile.Collision = false;
newTile.areaLevel = 0;
mapData.tiles[count] = newTile;
count++;
}
}

for (int y = 0; y < mapData.mapHeight; y++)
{
for (int x = 0; x < mapData.mapWidth; x++)
{
//display the images somehow with a link to the array key so i can change them when i click 'm.
}
}


}
}
}


I am trying to work this out first then add the NPC, object, chest, etc layers.

Now how to get this data, or atleast the array key and image to display properly in the map editor. Next step is adding menu's for actually picking brushes and changing properties on those tiles.
Ok, finally i got something going, but now i lost my scroll bars to navigate the map. i'm also clueless how to edit the tiles but i hope i can somehow click the rectangles to get the array key.

Some help is really appreciated as this is taking way too long :D.

-edit-
I fixed the scrollbar issue with the min scroll size, a 1000*1000 map is a tad slow so i will be searching on how to get the location of the window relative to the map and render only those tiles which "should" not be to mutch of a hassle as i done this plenty enough within XNA games. What i do need to know how i can communicate with the tiles i have drawn, if it's even possible this way. I somehow need to get the array key of the rectangle my mouse is over and on click change that tile.


public partial class mapEditor : Form
{
MapData mapData;
Rectangle[] tileRectangles;

Image grid = Image.FromFile("f:/temp/grid.png");

public mapEditor(MapData mapData)
{

InitializeComponent();


tileRectangles = new Rectangle[mapData.mapWidth * mapData.mapHeight];

mapData.tiles = new TileData[mapData.mapWidth * mapData.mapHeight];

int count = 0;
for (int y = 0; y < mapData.mapHeight; y++)
{
for (int x = 0; x < mapData.mapWidth; x++)
{
TileData newTile = new TileData();
newTile.TextureName = "grid.png";
newTile.Collision = false;
newTile.areaLevel = 0;
mapData.tiles[count] = newTile;
count++;
}
}

count = 0;
for (int y = 0; y < mapData.mapHeight; y++)
{
for (int x = 0; x < mapData.mapWidth; x++)
{
Rectangle rec = new Rectangle(x * mapData.tileWidth, y * mapData.tileHeight, mapData.tileWidth, mapData.tileHeight);
tileRectangles[count] = rec;
count++;

}
}


this.mapData = mapData;
this.Paint += new PaintEventHandler(this.MyPaintEventHandler);
}


private void MyPaintEventHandler(object sender, PaintEventArgs args)
{
foreach (Rectangle rect in tileRectangles)
{
args.Graphics.DrawImage(grid, rect);
}
}

}
First off, you don't need pb.

Second, don't use a 1d array, thats just silliness, use a 2D array, it will make your code infinitely more readable with no drawbacks. Actually, I would do a list of lists ( List <List<TileData> > ) so you can dynamically grow or shrink based on the actual number of tiles used.

Third, in your double nested for loop, don't loop through the entire mapData, just the portion that is visible.

If you are using a single array because you are unfamiliar with multidimensional arrays, read this.

First off, you don't need pb.

Second, don't use a 1d array, thats just silliness, use a 2D array, it will make your code infinitely more readable with no drawbacks. Actually, I would do a list of lists ( List <List<TileData> > ) so you can dynamically grow or shrink based on the actual number of tiles used.

Third, in your double nested for loop, don't loop through the entire mapData, just the portion that is visible.

If you are using a single array because you are unfamiliar with multidimensional arrays, read this.


Thanks,

Why are 2D arrays more readable? I am moving allong my maps just fine, have always used it:
up = -mapwidth
right = +1
down = +mapwidth
left = -1

Very easy to understand for me and have been using those a lot. I have worked with arrays that have many dimensions so i have no problems using those.

also, 2+D arrays, arrays within arrays and lists within lists will not serialize correctly during runtime for the xbox, so i will never be able to save map data. I'm planning on using a binary writer and on map loading compare it with the map (check the binary if a certain chest has been opened for instance) but i might want to save map data later on and then i have to redesign my whole editor and game.

Like i said i will be making functionality for just showing the tiles needed to be seen, but wanted to know first if it really had perforcmance issues, since this aint the actual game i can get away with some ineficiency.


But i really like answers on my question about how to acces my tiledata, I also noticed while scrolling i get some weird artifacts like double maps and overlapping maps...... this is getting a real pain...

This does not clear up the artifacts...

if (ScrollStateUserHasScrolled > 0)
{
this.Refresh();
}
Well i guess i have to do some winform tutorials for this as i wasted another couple of hours without any progress. I just put this on hold as i feel i am reinventing the wheel and just build a good and user friendly editor in XNA. But i definatly have to look into winforms some time as this could save me many hours as i don't have to build my own UI.

This topic is closed to new replies.

Advertisement