Thread: Creating a 2-D tile system...

  1. #1
    Registered User
    Join Date
    Nov 2005
    Posts
    673

    Creating a 2-D tile system...

    Some of you may remember my question about structs yesterday. Well I having trouble creating my system for the tiles.
    I came up with this but it doesnt work. The program crashes everytime I try to run the program.
    Code:
    #ifndef CLASS_HPP
    #define CLASS_HPP
    #include <iostream>
    #include <fstream>
    #include <string>
    #include <windows.h>
    #include <vector>
    #include <memory>
    const int MAX_MAP_SIZE = 256;
    struct Tiles
    {
        bool Walkable;
        bool Trigger;
        bool RunScript;
        char Symbol;
        std::string ScriptFile;
        short TriggerNumber;
    };
    
    class X_Map
    {
    public:
        X_Map();
        ~X_Map();
        bool NewMap(const std::string& names, short x_size, short y_size);
        bool LoadMap();
        bool SaveMap();
    
        long GetMapSize();
    protected:
        Tiles* MapTiles[256][256];
    };
    
    class MapMgr : public X_Map
    {
    public:
        MapMgr();
        ~MapMgr();
        void gotoxy(int xpos, int ypos);
    
        bool GetWalkableStatus(short x, short y);
        void ChangeWalkableStatus(bool status, short x, short y);
    
        char GetTileSymbol();
        void SetTileSymbol(char tile, short x, short y);
    
        bool GetTriggerStatus(short x, short y);
        void ChangeTriggerStatus(bool status, short x, short y);
        void ChangeTriggerNumber(short trig_num, short x, short y);
    
        bool GetScriptStatus(short x, short y);
        void ChangeScriptStatus(bool status, short x, short y);
        void SetScriptFile(const char* scriptfile);
    };
    #endif
    and when I try to do this... it gives these errors..
    Code:
    C:\C++Projects\Test\class.hpp:31: error: `new' cannot appear in a constant-expression
    C:\C++Projects\Test\class.hpp:31: error: ISO C++ forbids initialization of member `MapTiles'
    C:\C++Projects\Test\class.hpp:31: error: making `MapTiles' static
    C:\C++Projects\Test\class.hpp:31: error: invalid in-class initialization of static data member of non-integral type `Tiles*'
    :: === Build finished: 4 errors, 0 warnings ===
    Code:
    class X_Map
    {
    public:
        X_Map();
        ~X_Map();
        bool NewMap(const std::string& names, short x_size, short y_size);
        bool LoadMap();
        bool SaveMap();
    
        long GetMapSize();
    protected:
        Tiles* MapTiles = new Tiles[256][256];
    };
    I really am lost on ideas, so I came here as a last resort. I greatly appreciate any assistance.

  2. #2
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    new should be in the constructor - not in the class declaration ( and delete in the destructor)
    you should fix your type for the MapTiles. Is it 2D array?

    To allocate 2D array dynamically - use loop. There are samples on the board (and even maybe in the FAQ)
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  3. #3
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    You said in your previous thread that you use vectors a lot, but that you don't think they are needed here. If you are going to use new to allocate your array (which apparently you have to do because it is so big), then just use vector:
    Code:
    std::vector<std::vector<Tiles> > MapTiles;
    Code:
    X_Map() : MapTiles(256, std::vector<Tiles>(256)) { }

  4. #4
    Registered User
    Join Date
    Nov 2005
    Posts
    673
    Vart: Yes it is a 2-D array
    Daved: Thank you for that. I havent used 2-D vectors as much. I guess I will use them to get a hold on things?

    Vart how would I place new in the constructor? would that not just create a local object within the constructor?

    Thank you both for your assistance.

  5. #5
    l'Anziano DavidP's Avatar
    Join Date
    Aug 2001
    Location
    Plano, Texas, United States
    Posts
    2,743
    If you want to continue to use a normal array like you are doing now, you should do somethiing like this...

    In your class declaration, instead of this:

    Code:
    Tiles* MapTiles[256][256];
    You need this:

    Code:
    Tiles** MapTiles;
    OR you could do this:

    Code:
    Tiles MapTiles[256][256];
    Either way would work, although they are slightly different implementations. To learn about the differences between each implementation, find a good book on C++ and pointers, or read the FAQ as I am sure it talks about it.

    Next, if you do it in the first way (using a Tiles** method), then in the constructor you need to make sure that you allocate the memory. You cannot do this in the class declaration, but only in the class definition, aka where you define the function bodies.

    It would look something like this:

    Code:
    X_Map()::X_Map()
    {
    	MapTiles = new Tiles* [256];
    	for ( int x = 0; x < 256; x++ )
    		MapTiles[x] = new Tiles[256];
    }
    Then in the destructor you need to make sure you delete all that memory that you have just allocated.
    My Website

    "Circular logic is good because it is."

  6. #6
    Registered User
    Join Date
    Nov 2005
    Posts
    673
    I came up with this. using the vector idea
    Code:
    struct Tiles
    {
        bool Walkable;
        bool Trigger;
        bool RunScript;
        char Symbol;
        std::string ScriptFile;
        short TriggerNumber;
    };
    
    class X_Map
    {
    public:
        X_Map();
        ~X_Map();
        bool LoadMap(const std::string&);
        bool SaveMap();
    
        bool SetMapName(const std::string&);
        std::string GetMapName();
    
        long GetMapSize();
    protected:
        short *Size_X;
        short *Size_Y;
    
        std::string MapName;
        std::vector<std::vector<Tiles> > MapTiles;
    };
    and the constructor
    Code:
    X_Map::X_Map() : MapTiles(*Size_X, std::vector<Tiles>(*Size_Y))
    {
        for ( int x = 0; x < *Size_X; ++x )
        {
            for ( int y = 0; y < *Size_Y; ++y )
            {
                MapTiles[x][y].Walkable = true;
                MapTiles[x][y].Trigger = false;
                MapTiles[x][y].RunScript = false;
                MapTiles[x][y].Symbol = 'N';
                MapTiles[x][y].TriggerNumber = 0;
                MapTiles[x][y].ScriptFile = "NULL.xts";
            }
        }
    }
    also how do I get a sizeof() the entire vector. do I get the sizeof each element individually and just add them all together? Thank you all for the help.

  7. #7
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    What are you doing with Size_X and Size_Y? Why are they pointers? At the place you are using them, then are uninitialized. Did you want to have a constant size (like 256)? Did you want to let the user specify the size (if so you'd want to have parameters in the constructor).

    To get the size of a single vector, use the size() member function. Since all of the nested vectors should be the same size, you can get the size of the fist nested vector with size(). Multiply the two together to get the total number of elements. Of course, since you are setting the size of the vectors yourself, then you already know they will be 256x256 or Size_X * Size_Y.

  8. #8
    Registered User
    Join Date
    Nov 2005
    Posts
    673
    Size_X and Size_Y are initialized I think.
    in this beat of code.
    Code:
    class MapMgr : public X_Map
    {
    public:
        MapMgr(short SizeX, short SizeY);
    //
    and the code
    Code:
    MapMgr::MapMgr(short SizeX, short SizeY)
    {
        Size_X = new short;
        Size_Y = new short;
        *Size_X = SizeX;
        *Size_Y = SizeY;
    }
    I was thinking if the X_Map is the base of MapMgr, then MapMgr's constructor would be called prior to X_Map. Am I correct in thinking this? If not please correct me.

  9. #9
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    Actually that's not correct. The base class constructor is called first. Besides, you don't want your base class to rely on anything about the derived class.

    I would suggest making the X_Map class take the sizes in its constructor and have the MapMgr class passthe values to it. Do you know how to call a base class constructor in an initialization list?

    Also, there is no reason to use new with those Size variables. Make them regular members and not pointers.

  10. #10
    Registered User
    Join Date
    Nov 2005
    Posts
    673
    Sadly no I dont know much about the function of classes. Just basic stuff.

  11. #11
    l'Anziano DavidP's Avatar
    Join Date
    Aug 2001
    Location
    Plano, Texas, United States
    Posts
    2,743
    Yeah change those Size_X and Size_Y variables to be regular variables, and not pointers. There is no need for them to be pointers.

    Then that one line can look like:

    Code:
    X_Map::X_Map( int Size_X, int Size_Y ) : MapTiles(Size_X, std::vector<Tiles>(Size_Y) )
    My Website

    "Circular logic is good because it is."

  12. #12
    Registered User
    Join Date
    Nov 2005
    Posts
    673
    That line is confusing to me. Does making the parameter names the same as the member names set the members to the value given in the parameter? Also how do I call the X_Map with parameters if X_Map is the base class and I am doing this to create the map
    Code:
    MapMgr* T = new MapMgr;
    just looking for a little clarification, thanks a lot.

  13. #13
    l'Anziano DavidP's Avatar
    Join Date
    Aug 2001
    Location
    Plano, Texas, United States
    Posts
    2,743
    It was just an example. Change it to something else, whatever else you want it to be, like this:

    Code:
    X_Map::X_Map( int sX, int sY ) : MapTiles(sX, std::vector<Tiles>(sY) )
    You would still need a default constructor, however. Therefore, in X_Map, it would look something like this:

    Code:
    class X_Map
    {
    public:
    
    	X_Map ( );
    	X_Map ( int sX, int sY );
    
    	~X_Map ( );
    
    	...the rest of your functions...
    
    private:
    
    	int Size_X, Size_Y;
    	vector < vector < Tiles > > MapTiles;
    }
    In MapMgr you can do it many different ways...but whatever you do, your MapMgr constructors need to call the constructors of its parent class.
    My Website

    "Circular logic is good because it is."

  14. #14
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    And all of this is getting you nowhere close to having a fully functional tile system. You need to re-think your approach to this. Does every single tile really need to be an object with all those variables?

    Creating classes that operate on a 2D array or a linear array is going to be much simpler since a tile map can easily be represented by this. A movement map can be the same size as the tile map and would represent which tiles could be moved onto and which ones acted as walls.

    The way I approach it is that layer 0 or map 0 is always the movement map for the entire layered map. Layer 1 is the ground tiles, layer 2 would be tiles of objects above the ground layer, layer 3 could be overhangs, and other layers would be to add other effects.

    I maintain a vector of CMap2D objects which represents the entire map. CMap2D is managed by CMap2DMgr which must be used to gain access to any of the maps. No one else in the system touches the maps or has permission to. The tiles are stored in a vector in the editor portion and in an array in the actual engine portion. Each map value in the CMap2D arrays directly maps to the values in the CTileMgr vector/array so that the map is essentially paint by number. Animated tiles can be achieved by altering map values, thus images, over time. This is controlled in my system by a tile animation class that monitors one or multiple offsets as well as a map number (the layer) and also has an array of values that represent the frames. The class simply updates the animation based on time and alters the current value in the map via CMapMgr and the end result is you can have animated tiles. Sprites are completely independent of the tile maps and are controlled by a totally separate class. Sprites can be any size and are only bound to the underlying maps in as much as it affects their movement. Size of sprites is not bound to nor snapped to the underlying map grid.

    There are several other approaches such as linked lists and so forth but I've found working with the maps as arrays is extremely simple.

    Whichever approach you take I recommend you figure out your entire system or at least scratch it out on paper or in a text document prior to writing any code. Taking random approaches and random stabs at this or that is only going to lead you into frustration. Once you figure out exactly what type of system you want and how it works, code it, and come back here for questions. Right now you are just shooting in the dark and all of our suggestions although very good may or may not align with your particular setup.
    Last edited by VirtualAce; 05-24-2007 at 09:29 AM.

  15. #15
    Registered User
    Join Date
    Nov 2005
    Posts
    673
    Bubba, do you mind me asking if you could give an example of what you speak of. I understand, but when I try to design stuff prior to coding I never can seem to get my words to make sense later. I would be greatly indebted if you could assist me. I have been trying to get this one thing right for almost 2 weeks now, and I am getting frustrated bit by bit.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Smooth walking on tile based map system
    By abraham2119 in forum C Programming
    Replies: 8
    Last Post: 07-10-2009, 10:33 AM
  2. New system build wont boot
    By lightatdawn in forum Tech Board
    Replies: 7
    Last Post: 12-02-2005, 06:58 AM
  3. Using mscorlib & system namespace in an MFC?
    By Robert_Sitter in forum C++ Programming
    Replies: 3
    Last Post: 11-13-2005, 06:47 PM
  4. Ideas in creating a password system
    By subdene in forum C++ Programming
    Replies: 7
    Last Post: 02-23-2002, 04:34 AM
  5. Creating an Instant Messenging system...
    By Fool in forum C++ Programming
    Replies: 8
    Last Post: 08-13-2001, 06:41 PM