C# woes
Moderator: Moderators
C# woes
Hello all!
I'm trying to ultimately program a NES metatile editor with C# for this game I've been working on.. the only thing thus far I've been able to complete was drawing an 8x8 tile to a picturebox.. The whole C# class/stuct stuff really confuses me but I have a basic understanding of it. My question is what steps would I go through to program a routine to draw an entire CHR page rather than a single 8x8 tile.. It's probably simple and I'm over thinking it, but, I dunno. Than, I would like to know how to arrange those into an array of 8x8 tiles where I could draw a 16x16 tile by saying upper left for 16x16 tile 0x02 is 8x8 tile 0x1C and so on..
Thanks.
I'm trying to ultimately program a NES metatile editor with C# for this game I've been working on.. the only thing thus far I've been able to complete was drawing an 8x8 tile to a picturebox.. The whole C# class/stuct stuff really confuses me but I have a basic understanding of it. My question is what steps would I go through to program a routine to draw an entire CHR page rather than a single 8x8 tile.. It's probably simple and I'm over thinking it, but, I dunno. Than, I would like to know how to arrange those into an array of 8x8 tiles where I could draw a 16x16 tile by saying upper left for 16x16 tile 0x02 is 8x8 tile 0x1C and so on..
Thanks.
Re: C# woes
Are you trying to draw a whole tile table, or a whole screen out of tiles?zkip wrote:What steps would I go through to program a routine to draw an entire CHR page rather than a single 8x8 tile
Personally, I tend to use very simple metatile structs.
Code: Select all
struct MetaTile
{
public byte topLeft;
public byte topRight;
public byte bottomLeft;
public byte bottomRight;
}
As far as drawing screens made out of tiles, I've written plenty of NES level editors, and in my experience, GDI+ simply isn't fast enough with tiled images to make anything more than a bare-bones quick-and-dirty editor. If that's what you're after then you're all set. For my needs I ended up writing my own drawing code that supports drawing tile-based images with palettes. If you're interested, I'd be glad to share the code.
As far as actually drawing things out, I tend to use a set of classes along these lines:
- GameScreen - Represents the data that defines a screen. The actual data would vary depending on whether you're using an object-based layout, a metatile-based layout, or something else.
- NameTable - Represents an NES name table (32x30 tiles), tile and attribute data. The GameScreen would "draw" itself to the NameTable by telling it which tiles go where.
- ScreenRenderer - Takes a NameTable and a tile source and renders a screen image to a bitmap.
If it sounds like I'm severely over-thinking things (yeah... I tend to do that on occasion), it basically breaks down to this:
Code: Select all
class Example {
byte[,] ntTiles = new byte[32,30];
byte[,] ntPalettes = new byte[32,30]; // If applicable
Bitmap screenImage = new Bitmap(256, 240, 32bppwhatever);
Graphics gScreenImage; // Initialize with screenImage (if you're using GDI)
void DrawScreen() {
// lay out your nametable here
}
void RenderScreen() {
for(tileY as int = 0; tileY < 30; tileY++) {
for(tileX as int = 0; tileX < 32; tileX++) {
DrawTile(tileX * 8, tileY * 8, ntTiles[tileX, tileY], ntPalettes[tileX, tileY]);
}
}
}
void DrawTile(blah blah) {
// It sounds like you've got this figured out
}
}
Re: C# woes
Thanks a lot for writing such a detailed response.. but honestly I'm still sort of lost and so confused...
Yes, eventually my plan is a level editor but at the moment, I just need this metatile editor.
Also,
What exactly goes here??
Here is my 8x8 tile drawing routine:
I'm not sure how to incorporate this into what you're talking about. I'm terribly sorry for the bother, but I'd really like to fully understand this.
Yes, eventually my plan is a level editor but at the moment, I just need this metatile editor.
Also,
Code: Select all
void DrawScreen() {
// lay out your nametable here
}
Here is my 8x8 tile drawing routine:
Code: Select all
private Bitmap _8x8(byte[] tileDat, Bitmap canvas, Color[] palDat)
{
byte plane0;
byte plane1;
byte final;
for (int y = 0; y < canvas.Height; y++)
{
plane0 = 0;
plane1 = 0;
for (int x = 0; x < canvas.Width; x++)
{
plane0 = 0;
plane1 = 0;
plane0 = Convert.ToByte(((tileDat[y]) & (0x80 >> x)) >> (7 - x));
plane1 = Convert.ToByte(((tileDat[y + 8]) & (0x80 >> x)) >> (7 - x));
final = Convert.ToByte((plane1 << 1) + plane0);
canvas.SetPixel(x, y, palDat[final]);
}
}
return canvas;
}
Re: C# woes
Well then you can safely ignore most of what I said for now. One thing I'll re-emphasize is that GDI+ is a little slow, but sometimes workable. On the other hand, using SetPixel makes GDI+ look like greased lightning. If you really want to avoid the much-more-complicated but oh-so-much-faster approach of directly modifying raw image data, then you might be able to get away using SetPixel to load the tiles. You'll have to try and see how fast it is.zkip wrote: Yes, eventually my plan is a level editor but at the moment, I just need this metatile editor.
I would recommend drawing all your tiles to an image that is 8 pixels wide and 2048 (8 px * 256 tiles) pixels tall. Basically, you want to stack all your tiles together vertically. That way the location for tile x would be (0, 8 * x).
It looks like you're already almost there. All you need to do is accept a tile number as a parameter and adjust where you draw your pixels based on the tile number. If you use a vertical arrangement like I described:
Code: Select all
canvas.SetPixel(x, y + tileNumber * 8, palDat[final]);
Code: Select all
Bitmap Load_8x8(int tileNum, byte[] rom, int tileOffset, Bitmap canvas, Color[] palDat)
{
byte plane0;
byte plane1;
byte final;
for (int y = 0; y < 8; y++)
{
for (int x = 0; x < 8; x++)
{
plane0 = Convert.ToByte(((rom[tileOffset + y]) & (0x80 >> x)) >> (7 - x));
plane1 = Convert.ToByte(((rom[tileOffset + y + 8]) & (0x80 >> x)) >> (7 - x));
final = Convert.ToByte((plane1 << 1) + plane0);
canvas.SetPixel(x, y + tileNum * 8, palDat[final]);
}
}
return canvas;
}
Code: Select all
void LoadWholeChrPage(int chrOffset) {
Bitmap chr = new Bitmap(8, 2048, PixelFormat.32bitgoodness);
for(i as integer = 0; i < 256; i++) {
int tileOffset = chrOffset + i * 16;
Load_8x8(i, _theRom, tileOffset, chr, _thePalette);
}
}
Re: C# woes
Unless you absolutely want to write your own editor, you could try MapEd Pro, it has a metatile editor.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
Re: C# woes
Ugh, I'm terribly sorry.. I just can't grasp anything .. I finally understand this, however, now I can't figure out how to define each 8x8 as separate tiles to use to construct a 16x16 tile in a separate picture box. Would this be the metatile structs you referred to earlier?
Thanks again, and sorry for so much bother.
Thanks again, and sorry for so much bother.
Re: C# woes
It sounds like you're pretty new to C#. I'm not sure whether you're getting stuck on the C# side of things or the underlying concepts. I don't mind in the least trying to help, but right now I'm just basically guessing which blanks need to be filled in.
I know you probably already understand this, but just to walk through the logic... A complete CHR table contains 256 tiles. Each tile is identified by its index. So if you load your tiles into a vertical strip, the top tile would be tile 0, the next one down would be 1, and so on.
The metatile structure simply stores the four tile numbers that make up a metatile. So to store all your actual metatile data you could declare an array of MetaTile or a List<MetaTile>, depending on which one suits your needs better. An array might be simpler if you want a fixed number of metatiles. A List<MetaTile> would be easier if you want to be able to add/remove/insert MetaTile entries.
The trickier part is presenting the data in the UI and letting the user manipulate it. If you want to draw a metatile, you'll probably want to start with a function that draws an individual tile by index.
Then you can simply call this function four times to draw a metatile.
Hopefully that clears things up a bit.
I know you probably already understand this, but just to walk through the logic... A complete CHR table contains 256 tiles. Each tile is identified by its index. So if you load your tiles into a vertical strip, the top tile would be tile 0, the next one down would be 1, and so on.
The metatile structure simply stores the four tile numbers that make up a metatile. So to store all your actual metatile data you could declare an array of MetaTile or a List<MetaTile>, depending on which one suits your needs better. An array might be simpler if you want a fixed number of metatiles. A List<MetaTile> would be easier if you want to be able to add/remove/insert MetaTile entries.
Code: Select all
List<MetaTile> metatiles = new List<MetaTile> ()
void Example() {
// Create and initialize a metatile and add it to the list
MetaTile mt = new MetaTile();
mt.topLeft = 0x40;
mt.topRight = 0x41;
mt.bottomLeft = 0x50;
mt.bottomRight = 0x51;
metatiles.Add(mt);
}
Code: Select all
void DrawTile(Graphics target, byte tileIndex, int x, int y) {
// Determine where we will grab the tile from, assuming tiles are stored in a vertical strip
var sourceRect = new Rectangle(0, tileIndex * 8, 8, 8);
// Determine where it will be drawn
var destRect = new Rectangle(x, y, 8, 8);
// Draw it
target.DrawImage(tileSourceImage, destRect, sourceRect);
}
Code: Select all
void DrawMetatile(MetaTile mt, int x, int y) {
DrawTile(mt.TopLeft, x, y);
DrawTile(mt.TopRight, someX, someY);
// ... and two more times for the bottom tiles
}
void Usage() {
// Draw the first metatile (number 0) at the location 0,0
DrawMetatile(metatiles[0], 0, 0);
}
Re: C# woes
Thank you so much snarfblam. I owe you man. I do have a few more questions though;
This is working like I wanted it to, however, any suggestions as to how to get everything all cleaned up and how to arrange it to where I have a page of metatiles displayed? Such as rows? I.E Row 0 contains metatiles 0-A etc.
Edit: Erm.. I see what you mean now about the slowness.. It takes ~20 secs. to draw 49 metatiles.. I'd like for you to explain the faster method if possible. Thanks again.
This is working like I wanted it to, however, any suggestions as to how to get everything all cleaned up and how to arrange it to where I have a page of metatiles displayed? Such as rows? I.E Row 0 contains metatiles 0-A etc.
Edit: Erm.. I see what you mean now about the slowness.. It takes ~20 secs. to draw 49 metatiles.. I'd like for you to explain the faster method if possible. Thanks again.
Re: C# woes
How are you drawing the tiles? In my custom tool I can draw a whole screen of 240 16x16 metatiles many times per second without any slowdown at all, so it shouldn't take long to draw 50 metatiles. Have you tried profiling your code to find out where it's taking so long?
Re: C# woes
In C#, if you still want to use the awful System.Drawing way of drawing images, at least use LockBits as your way to draw stuff.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
Re: C# woes
Basically, I'm doing nothing more than what is already shared here in this thread... the 8x8 function to render the 8x8 and the DrawTile, and DrawMetatile functions from snarfblam to construct the full metatile.
Anyways, here is the whole shabang complete with trimmings.
Anyways, here is the whole shabang complete with trimmings.
Re: C# woes
Don't use SetPixel, it's too slow. Instead use LockBits and Marshal.Copy along with an int array.
Something like this:
If you already have a bitmap to store the new image into, there's no need to create a new one.
Something like this:
Code: Select all
int[] pixels = new int[128 * 128];
//your code here, set the pixels. A value is like R + (G << 8) + (B << 16). The index in the array = X + Y * width
Bitmap bmp = new Bitmap(128,128, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
var bits = bmp.LockBits(new Rectangle(0, 0, 128, 128), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
Marshal.Copy(pixels, 0, bits.Scan0, pixels.Length);
bmp.UnlockBits(bits);
//assign bmp somewhere like to a picture box or panel
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
Re: C# woes
Erm.. could it really be ONLY the SetPixel doing this?? I just tried a class from here. And it seems like it's even slower...
By doing this
in the Load_8x8 function and using that class it still hasn't loaded yet so it's slower apparently.....
By doing this
Code: Select all
LockBitmap lockBitmap = new LockBitmap(canvas);
lockBitmap.LockBits();
lockBitmap.SetPixel(x, y + tileNum * 8, palDat[final]);
lockBitmap.UnlockBits();
Re: C# woes
Basically, you need to make ALL your drawing happen on the int array, then at the very very end, you copy that to a bitmap. So you won't be making a 8x2048 image, and doing rectangle copies or anything like that.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
Re: C# woes
Argh... I'm so confused.. I'm sorry, but then what exactly do I need to do. Get rid of the .DrawImage parts?? I'm so lost..Dwedit wrote:Basically, you need to make ALL your drawing happen on the int array, then at the very very end, you copy that to a bitmap. So you won't be making a 8x2048 image, and doing rectangle copies or anything like that.
I'm sorry for so much bother on everyone and thanks.