It is currently Thu Oct 19, 2017 2:23 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: C# woes
PostPosted: Tue Nov 20, 2012 2:06 pm 
Offline

Joined: Tue Nov 20, 2012 1:59 pm
Posts: 65
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.


Top
 Profile  
 
 Post subject: Re: C# woes
PostPosted: Tue Nov 20, 2012 3:53 pm 
Offline

Joined: Fri May 13, 2011 7:36 pm
Posts: 143
zkip wrote:
What steps would I go through to program a routine to draw an entire CHR page rather than a single 8x8 tile


Are you trying to draw a whole tile table, or a whole screen out of tiles?

Personally, I tend to use very simple metatile structs.
Code:
struct MetaTile
{
    public byte topLeft;
    public byte topRight;
    public byte bottomLeft;
    public byte bottomRight;
}

Some utility functions to extract metatiles from a byte array (and/or stream) and write them back are usually handy and simple to write.

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.

Again, if you're doing something Q&D, you might not want to spend a ton of time architecturing. You could just declare a couple of arrays to represent your nametable and write a single function that loops over the nametable and draws each tile.

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:
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
    }
}


Top
 Profile  
 
 Post subject: Re: C# woes
PostPosted: Tue Nov 20, 2012 5:51 pm 
Offline

Joined: Tue Nov 20, 2012 1:59 pm
Posts: 65
Thanks a lot for writing such a detailed response.. but honestly I'm still sort of lost and so confused... :x
Yes, eventually my plan is a level editor but at the moment, I just need this metatile editor.

Also,
Code:
    void DrawScreen() {
        // lay out your nametable here
    }

What exactly goes here??

Here is my 8x8 tile drawing routine:
Code:
        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;
        }

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.


Top
 Profile  
 
 Post subject: Re: C# woes
PostPosted: Wed Nov 21, 2012 6:57 am 
Offline

Joined: Fri May 13, 2011 7:36 pm
Posts: 143
zkip wrote:
Yes, eventually my plan is a level editor but at the moment, I just need this metatile editor.

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.

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:
canvas.SetPixel(x, y + tileNumber * 8, palDat[final]);


I personally load the whole ROM into a single byte array, so if I snarf-ified your code a little it would look like this:
Code:
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;
        }

Then you can load a whole page of contiguous CHR like so:
Code:
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);
    }
}


Hope that's a little more helpful.


Top
 Profile  
 
 Post subject: Re: C# woes
PostPosted: Wed Nov 21, 2012 7:41 am 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2962
Location: Tampere, Finland
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: kkfos.aspekt.fi


Top
 Profile  
 
 Post subject: Re: C# woes
PostPosted: Wed Nov 21, 2012 3:03 pm 
Offline

Joined: Tue Nov 20, 2012 1:59 pm
Posts: 65
Ugh, I'm terribly sorry.. I just can't grasp anything :oops: .. 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.


Top
 Profile  
 
 Post subject: Re: C# woes
PostPosted: Wed Nov 21, 2012 4:17 pm 
Offline

Joined: Fri May 13, 2011 7:36 pm
Posts: 143
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.

Code:
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);
}


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.
Code:
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);
}

Then you can simply call this function four times to draw a metatile.
Code:
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);
}


Hopefully that clears things up a bit.


Top
 Profile  
 
 Post subject: Re: C# woes
PostPosted: Wed Nov 21, 2012 7:30 pm 
Offline

Joined: Tue Nov 20, 2012 1:59 pm
Posts: 65
Thank you so much snarfblam. :D 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.


Top
 Profile  
 
 Post subject: Re: C# woes
PostPosted: Thu Nov 22, 2012 1:35 pm 
Offline

Joined: Mon Jun 27, 2011 4:14 am
Posts: 27
Location: Lurker Cave
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?


Top
 Profile  
 
 Post subject: Re: C# woes
PostPosted: Thu Nov 22, 2012 1:40 pm 
Offline
User avatar

Joined: Fri Nov 19, 2004 7:35 pm
Posts: 3943
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!


Top
 Profile  
 
 Post subject: Re: C# woes
PostPosted: Thu Nov 22, 2012 1:46 pm 
Offline

Joined: Tue Nov 20, 2012 1:59 pm
Posts: 65
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.


Top
 Profile  
 
 Post subject: Re: C# woes
PostPosted: Thu Nov 22, 2012 2:00 pm 
Offline
User avatar

Joined: Fri Nov 19, 2004 7:35 pm
Posts: 3943
Don't use SetPixel, it's too slow. Instead use LockBits and Marshal.Copy along with an int array.
Something like this:
Code:
            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


If you already have a bitmap to store the new image into, there's no need to create a new one.

_________________
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!


Top
 Profile  
 
 Post subject: Re: C# woes
PostPosted: Thu Nov 22, 2012 2:14 pm 
Offline

Joined: Tue Nov 20, 2012 1:59 pm
Posts: 65
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... :x

By doing this
Code:
                    LockBitmap lockBitmap = new LockBitmap(canvas);
                    lockBitmap.LockBits();
                    lockBitmap.SetPixel(x, y + tileNum * 8, palDat[final]);
                    lockBitmap.UnlockBits();

in the Load_8x8 function and using that class it still hasn't loaded yet so it's slower apparently.....


Top
 Profile  
 
 Post subject: Re: C# woes
PostPosted: Thu Nov 22, 2012 2:27 pm 
Offline
User avatar

Joined: Fri Nov 19, 2004 7:35 pm
Posts: 3943
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!


Top
 Profile  
 
 Post subject: Re: C# woes
PostPosted: Thu Nov 22, 2012 2:33 pm 
Offline

Joined: Tue Nov 20, 2012 1:59 pm
Posts: 65
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.

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.. :?

I'm sorry for so much bother on everyone and thanks.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group