[C#] RPG Structures

Started by G_G, November 30, 2010, 12:17:13 am

Previous topic - Next topic

G_G

This thread is for my personal needs. I am making structures or classes for basic RPG aspects and engine features. Or whatever you want to call it. I'll have different ideas and structures posted here. Basically I'll post each one here and see if there are ways to improve it, if my code is inefficient or not, or if I should scrap it and re-do it.


Tileset Class
Spoiler: ShowHide
Alright my tileset structure and tilemap is meant to be where you can use any tileset no matter how many tiles across, tiles up and down, tile size. So you could have a 20x5 tile set with each tile being 32x16. Or you could simple use a standard RMXP tileset an 8xY tileset. (Y being tiles vertical) With each tile being 32x32. Now I've got the tileset structure itself done.

Just throwing it here to see if its efficient or if its worthless or whatever. Now I've come up with a simple method to be able to grab any tile. So whatever tileset size or tile size you can still use it.
Heres an example tileset.

Now my method requires one argument. The tile id. Now the image has 16 numbers. Each tile is labeled with its own id. You call the GrabTile method with the id you want and then it uses an equation and returns a rectangle containing where the tile is and how big that tile is. Now the map file contains an array of layers. Each layer has an array of tile id's so when the tilemap draws the map out, it grabs tile id's from the map, grabs the rectangle from the method, then just draws the tile.
Grab Tile Method:
Arguments: tile_id
Variables:
-tile_id : int
-tiles_across : int
-tile_width : int
-tile_height : int
-tile_x : int
-tile_y : int
tile_y = tiles_across / tile_id * tile_height
while tile_id >= tiles_across
  tile_id -= tiles_across
end
tile_x = tile_id * tile_width


Heres the tileset class containing the grab tile method.
namespace Maze.Data
{
    [Serializable]
    public class Tileset
    {
        public int id = 0;
        public string name = "";
        public string file_name = "";
        public bool[] passage = { };
        public int[] terrain = { };
        public int[] depth = { };
        public int tiles_across = 4;
        public int tile_width = 32;
        public int tile_height = 32;
        public Rectangle GrabTile(int tile_id)
        {
            Rectangle area = new Rectangle(0, 0, tile_width, tile_height);
            area.Y = tile_id / tiles_across * tile_height;
            while (tile_id >= tiles_across)
            {
                tile_id -= tiles_across;
            }
            area.X = tile_id * tile_width;
            return area;
        }
    }
}


So if everything is correct, this virtually makes it possible to have any tileset you want. However making the tilemap and map editor to adjust to the freedom of this is going to be a bitch. Anyways thoughts?



Collision
Spoiler: ShowHide
In the engine there are objects and tiles. Each object and tile has its own area. The area is a rectangle which contains the x and y position of the object. The width and height variables are used as the end coordinate of the object. Now the area varies on the texture size or tile size of the object. Right now I only have came up with two collision methods and its the only ones I can think of as of right now.
These methods were created so I can keep a nice pixel movement in the engine.
Methods:
Object in Object
Object next to Object
public static AreaInArea(Rectangle area1, Rectangle area2)
{
    if (area1.X >= area2.X && area1.X <= area2.Width)
    {
        if (area1.Y >= area2.Y && area1.Y <= area2.Height)
        {
            return true;
        }
    }
    else if (area1.Width >= area1.X && area1.Width <= area2.Width)
    {
        if (area1.Height >= area2.Y && area1.Height <= area2.Height)
        {
            return true;
        }
    }
    return false;
}

public static bool AreaNextArea(Rectangle area1, Rectangle area2, int direction, int distance)
{
    switch (direction)
    {
        case 1:
            area1.X -= distance;
            area1.Y += distance;
            break;
        case 2:
            area1.Y += distance;
            break;
        case 3:
            area1.X += distance;
            area1.Y += distance;
            break;
        case 4:
            area1.X -= distance;
            break;
        case 6:
            area1.X += distance;
            break;
        case 7:
            area1.X -= distance;
            area1.Y -= distance;
            break;
        case 8:
            area1.Y -= distance;
            break;
        case 9:
            area1.Y -= distance;
            area1.X += distance;
            break;
    }
    return AreaInArea(area1, area2);
}

If the methods return true that means the area is in another area or the area is next to another area. The second method I'll use for checking to see if the tile next to the player is impassable or not.



If you think any of these can be improved, please let me know. I will be posting more structures and ideas as well later.

winkio

You might want to think of more object-oriented ways to do these, as they are much cleaner and more efficient:


Tileset: Make a tile class with a Texture2D field.  when you first read the map file, set the texture2D to the source data from the rectangle determined by your method from the tileset.  Then, whenever you want to draw the tile, just draw the Texture2D that it contains.


Collision: What you really want to do is just check the tiles immediately around the character for collisions, not every single tile on the map.  lets say your character is represented by the 8s and the tiles represented by 0s (passable) and 1s (solid):
0 0 0 0 0 0 0
0 0 1 0 0 0 0
0 0 8 8 0 0 0
0 1 8 8 0 0 0
0 1 1 0 0 0 0
0 0 0 0 0 0 0

You just need to get the coordinate of the 4 corners of the character area ((x1,y1)(x1,y2)(x2,y1)(x2,y2)) and then loop through the tiles corresponding to that area (int i = x1; i <=x2; i++) (int j = y1; j <=y2; j++).


Also, if you need a good reference for the ideas behind this stuff, there's a flash based reference that I found really useful a while back: http://www.tonypa.pri.ee/tbw/start.html  It's not quite the same as XNA, but it has some good ideas.

G_G

November 30, 2010, 01:33:12 am #2 Last Edit: November 30, 2010, 01:57:36 am by game_guy
Thanks winkio I'll take this advice into use. Any bit helps :)

EDIT: Alright I think I'm getting what you mean by the tile class. Or trying to grasp it anyways.

So I have my Tile class. The tile class has a texture2d which is the actual tile from the tileset correct? Now how exactly do I do that?

Then with the map data I'm basically assigning it an array of tiles instead of numbers. And its a 2d array so I can check it with player x and y basically correct?

winkio

QuoteSo I have my Tile class. The tile class has a texture2d which is the actual tile from the tileset correct? Now how exactly do I do that?

something like this:

Texture2D image;  // tileset image, set beforehand
Rectangle area = new Rectangle(32*i, 32*j, 32, 32); // source rectangle
int size = area.Width * area.Height; // area in pixels
Color[] data = new Color[size]; // texture data
image.GetData(0, area, data, 0, size); // gets data from source rectangle
Texture2D tileimage = new Texture2D(image.GraphicsDevice, area.Width, area.Height); // make new texture
tileimage.SetData<Color>(data); // set new texture data


QuoteThen with the map data I'm basically assigning it an array of tiles instead of numbers. And its a 2d array so I can check it with player x and y basically correct?

yeah.

G_G

November 30, 2010, 03:07:12 am #4 Last Edit: November 30, 2010, 03:09:29 am by game_guy
Sweet. Thanks winkio you have been so much help.

Another thing. Do you think my system will work for the tileset? I mean the any size tile and size tile set? Or do you think that'll cause too many problems.

winkio

November 30, 2010, 03:12:50 am #5 Last Edit: December 01, 2010, 11:45:37 pm by winkio
It should work well.  I actually used something similar in one of my old projects.  Actually, I'm developing something similar now, but you can define the complete geometry of tiles, so you can have ellipses, or triangles, or disjoint polygons, etc.

EDIT: also, just realized, it's easier just to store the source rectangle instead of making all those new textures.  Then you can use the source rectangle in the spritebatch.draw() method.

It looks like

Texture2D tileset;
Rectangle source;
Rectangle target;
Color color;
spritebatch.Draw(tileset, target, source, color);

G_G

December 02, 2010, 11:47:34 am #6 Last Edit: December 02, 2010, 12:11:50 pm by game_guy
Quote from: winkio on November 30, 2010, 03:12:50 am
It should work well.  I actually used something similar in one of my old projects.  Actually, I'm developing something similar now, but you can define the complete geometry of tiles, so you can have ellipses, or triangles, or disjoint polygons, etc.

EDIT: also, just realized, it's easier just to store the source rectangle instead of making all those new textures.  Then you can use the source rectangle in the spritebatch.draw() method.

It looks like

Texture2D tileset;
Rectangle source;
Rectangle target;
Color color;
spritebatch.Draw(tileset, target, source, color);



Thats what I was doing before.

EDIT: I actually perfectly see how I'm going to check tiles. I had the map structure containing integers for tiles which in my opinion was way easier. So I'll use my old way. However I'll use your method to check the tiles around it.

Grab the numbers from the surrounding tiles, input them through the tileset the map is using, then see if the tile is passable or not.

G_G

Is there a more efficient way of iterating through my tiles array? I feel the way I'm doing it can or will cause lag in the future.
for (int l = 0; l < map.layers; l++)
{
    for (int x = 0; x < map.width; x++)
    {
        for (int y = 0; y < map.height; y++)
        {
            Rectangle tile = GrabTile(map.tiles[l, x, y]);
            spriteBatch.Draw(tileset_graphic, new Vector2(x * tileset.tile_width, y * tileset.tile_height), tile, Color.White);
        }
    }
}

winkio

Keep an array of tile objects only big enough to cover the drawable area (or maybe a few more, depending on processing), and just update it whenever you scroll or change maps.

example:

// class variable initialized in constructor
Tile[,,] activeTiles = new Tile[map.layers, 22, 17];


public void UpdateTiles()
{
    x0 = 5;
    y0 = 12;
    for (int l = 0; l < map.layers; l++)
    {
        for (int x = 0; x < 22; x++)
        {
            for (int y = 0; y < 17; y++)
            {
                activeTiles[l,x,y] = GrabTile(map.tiles[l, x + x0, y + y0]);
            }
        }
    }
}
public void DrawTiles()
{
    for (int l = 0; l < map.layers; l++)
    {
        for (int x = 0; x < 22; x++)
        {
            for (int y = 0; y < 17; y++)
            {
                if (activeTiles[l,x,y] != null)
                    spriteBatch.Draw(tileset_graphic, new Vector2((x - 1) * tileset.tile_width, (y - 1) * tileset.tile_height), activeTiles[l,x,y], Color.White);
            }
        }
    }
}


G_G

Thanks winkio. Going to go implement that now. Then when my tilemap is completely done I'm going to implement that into the map editor. Scrolling is almost flawless right now. I can't wait to get the demo rolling out.

Blizzard

Actually (21, 16) is enough as only one additional tile will be visible at the time. :) But you would have to implement that in a slightly different way. It depends on you. You can always do this as an optimization later.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

winkio

Yeah, I just gave him an idea so that he wouldn't have to change too much.  In terms of speed efficiency, the best would be to keep the full array of tile objects, and just keep track of the starting position to draw.

tSwitch

I just have a List of tiles, and do a foreach through it.


FCF3a A+ C- D H- M P+ R T W- Z- Sf RLCT a cmn+++ d++ e++ f h+++ iw+++ j+ p sf+
Follow my project: MBlok | Find me on: tSwitch.us | Twitter | Tumblr