Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm developing a 2D tile based game and I have a few questions regarding it.

First I would like to know if this is the correct way to structure my Tile class:

namespace TileGame.Engine {
    public enum TileType {
        Air,
        Stone
    }

    class Tile {
        TileType type;
        bool collidable;

        static Tile air = new Tile(TileType.Air);
        static Tile stone = new Tile(TileType.Stone);

        public Tile(TileType type) {
            this.type = type;

            collidable = true;
        }
    }
}

With this method I just say world[y, x] = Tile.Stone and this seems right to me but I'm not a very experienced coder and would like assistance.

Now the reason I doubt this so much is because I like everything to be as optimized as possible and there is a major flaw in this that I need help overcoming. It has to do with saving and loading... well more on loading actually. The way it's done relies on the principle of casting an enumeration into a byte which gives you the corresponding number where its declared in the enumeration. Each TileType is cast as a byte and written out to a file. So TileType.Air would appear as 0 and TileType.Stone would appear as 1 in the file (well in byte form obviously). Loading in the file is alot different though because I can't just loop through all the bytes in the file cast them as a TileType and assign it:

for(int x = 0; x < size.X; x++) {
     for(int y = 0; y < size.Y; y+) {
          world[y, x].Type = (TileType)byteReader.ReadByte();
     }
}

This just wont work presumably because I have to actually say world[y, x] = Tile.Stone as apposed to world[y, x].Type = TileType.Stone. In order to be able to say that I need a gigantic switch case statement (I only have 2 tiles but you could imagine what it would look like with hundreds):

Tile tile;
for(int x = 0; x < size.X; x++) {
     for(int y = 0; y < size.Y; y+) {
          switch(byteReader.ReadByte()){
               case 0:
                    tile = Tile.Air;
                    break;
               case 1:
                    tile = Tile.Stone;
                    break;
          }
          world[y, x] = tile;
     }
}

Now you can see how unoptimized this is and I don't know what to do. I would really just like to cast the byte as a TileType and use that but as said before I have to say world[y, x] = Tile.whatever and TileType can't be used this way.

So what should I do? I would imagine I need to restructure my Tile class to fit the requirements but I don't know how I would do that. Please help!

Thanks.

share|improve this question
    
Nevermind the switch statement, you're already going to have a crazy amount of static field variables. Why do you want to do it that way? Why not just make the TileType a normal field variable and load it from binary as you said? Also, I don't see how this works at all: world[y, x] = Tile.Stone –  Richard Marskell - Drackir Jun 9 '12 at 4:34
    
I should have made this more clear but the world is a 2D array of Tile. –  MrPlosion1243 Jun 9 '12 at 15:28
    
@MrPlosion1243 I assume you also intended to show that you'd defined a few static objects: Air and Stone. –  Byte56 Jun 9 '12 at 15:49
add comment

2 Answers

up vote 5 down vote accepted

It's been a while since I used C#, but I think you can structure your class something like this:

public static class Tile {
    public static enum TileType {
        Air,
        Stone
    }
    public static bool IsSolid(TileType tile) {
        switch(tile) {
            case Air:
                return false;
            case Stone:
                return true;
        }
    }
}

Then just fill your world[y, x] with the type TileType and fill it up like so:

for(int x = 0; x < size.X; x++) {
     for(int y = 0; y < size.Y; y+) {
          world[y, x] = (Tile.TileType)byteReader.ReadByte();
     }
}

Then when you want to test collision you can just do this:

if(Tile.IsSolid(world[y,x])) {
   //collide with tile
}

Finally I don't think you should worry so much about optimizing right now. You can optimize forever. You'll never finish your game like that! You can worry about making it neat and clean, but not necessarily optimal. If something is really bugging you, just add a little comment for later defining how important you think the optimization is:

//TODO optimize this! Level 3

Then when you find things going slow, check your to-dos, tackling the highest level ones first.

share|improve this answer
5  
"You can optimize forever. You'll never finish your game like that!" so very true! –  Krom Stern Jun 9 '12 at 5:29
    
Ok I see. Quick question though should I do the same thing and have a switch case statement for something like if the tile does damage or the amount of damage it does or anything else where each tile needs specific data? –  MrPlosion1243 Jun 9 '12 at 15:25
    
Yep, as long as that information is general to the tile type and not to the specific tile. If you want information to be specific to the tile you'll either have to create a second array to hold that or you'll need to use something other than TileType in your world array. But everything that is common among all tiles of a specific type should be handled the same way as the solid attribute. –  Byte56 Jun 9 '12 at 15:45
    
Ok I have a better understanding now thanks. –  MrPlosion1243 Jun 9 '12 at 16:36
add comment

There is no right way to optimize loading code other than simply compressing your files. We've benchmarked and shown that loading uncompressed TGA files (textures that can be directly loaded into the GPU with no CPU transformation necessary) is waaaay slower than loading compressed PNGs (which need several CPU-side passes to decompress before loading into the GPU).

The simple fact is that your CPU is several orders of magnitude faster than your storage device (even SSDs), and any loading/saving code is going to be I/O bound, not CPU bound.

Instead of worrying about byte conversions, worry about making your I/O code streamable. Meaning, process data as it is read in, rather than needing to wait for it all to be loaded and then processing it. The processing time will then end up being completely hidden by the disk I/O.

I'm not a C# expert so I don't know the proper APIs off the top of my head. I do know that the APIs are available, and Googling ".net streaming io" should find them for you.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.