Thread: Level Editor in C++ / OOP Design

  1. #1
    Registered User \007's Avatar
    Join Date
    Dec 2010
    Posts
    179

    Level Editor in C++ / OOP Design

    Alright-- so I am designing a level editor. Let me first say this is the first game programming I have ever done. Better yet it's the first large C++ thing I have done. I have done a basic tiler/editor already and it is over a thousand lines of code. It's pretty messy. I am thinking of re-doing most of it.

    I was wondering if this sounds like something that would work, or better yet-- is it something a seasoned OO programmer would do.

    I am thinking of making a 'base class' called Object. This base class would contain a protected variable which contains the x and y coordinates for the object. This base class would contain a method called show() to display the object, and a method called get_x() and get_y() to get the cords.

    This is simple, from here I was thinking of deriving my "Tile" class from the Object class. Since every tile needs cords and also uses show() etc. The tile 'is an' object.

    I would derive another class called Sprite. This would be my class for 'moveable' objects. Since the sprites contain cords as well you could say a Sprite 'is an' Object as well.

    I think Object would be a good base class and Tile and Sprite would make good derived classes.

    Does this sound about right? Currently I have these classes but they are all separate and it gets a bit messy.

  2. #2
    Registered User nathandelane's Avatar
    Join Date
    Dec 2010
    Posts
    9

    Wink Class Hierarchy

    So from what I understand you are saying that you have a class Object:
    Code:
    struct Coordinates
    {
    	int x;
    	int y;
    };
    
    class Object
    {
    private:
    	struct Coordinates coordinates;
    protected:
    	int get_x();
    	int get_y();
    public:
    	Object(int x, int y);
    	~Object();
    	virtual void show() = 0;
    };
    And after that you would have classes that derived from this class, namely Tile and Sprite:
    Code:
    class Tile : public Object
    {
    public:
    	Tile(int x, int y);
    	~Tile();
    	void show();
    };
    
    class Sprite : public Object
    {
    public:
    	Sprite(int x, int y);
    	~Sprite();
    	void show();
    };
    After that a Sprite would be defined as "moveable"? In my opinion this is probably not the best OO design. Since your Sprite and Tile are technically different things, there doesn't need to be a common base class, even though they both have x and y coordinates. In C++ I don't know whether it's common or not, but generally when I program I create pure virtual classes that act as interfaces for my classes. In OOP always program against an interface. Your idea of programming a single base class though is not far off. The main problem with it though IS that Tile is a "stationary" component, and Sprite is a "moveable" component. So instead of Object, create a base Tile and base Sprite class. These base classes should contain the full contract (all methods that every Sprite or every Tile should have). Then when you collect Sprites or Tiles into a vector, for example, you collect them by their base class. And you don't have to ever know what their exact implementation is, because they always define the same methods. Of course you'll need one collection for Sprites, and one collection for Tiles, but in the end this will be easier to understand, and simpler to implement IMO.
    Last edited by nathandelane; 04-05-2011 at 09:12 AM.

  3. #3
    Registered User \007's Avatar
    Join Date
    Dec 2010
    Posts
    179
    Hmm that was pretty much what I was thinking. A little more complicated since I am using a multimedia library (SDL) and building classes using some types etc. But the general idea is the same.

    I wasn't thinking of renaming Sprites as movable-- just in my head I thought an Object and Sprite are essentially the same thing. The only difference is a Sprite can be moved so needs to handle input. On the other hand a Tile is the same as an Object but the difference is a Tile doesn't need to be moved so doesn't need to have input handled.

    I guess I was over complicating my designs?

    I have all separate classes now and things work pretty nice. I am probably over thinking it and trying to optimize too much while I should be focusing on expanding the program, haha.

    How do you help manage your designs while expanding your application? I pretty much hit the end of my initial design plans and now I am expanding things, in other words I am winging it now.

  4. #4
    Registered User nathandelane's Avatar
    Join Date
    Dec 2010
    Posts
    9

    Thumbs up

    Quote Originally Posted by \007 View Post
    I wasn't thinking of renaming Sprites as movable-- just in my head I thought an Object and Sprite are essentially the same thing. The only difference is a Sprite can be moved so needs to handle input. On the other hand a Tile is the same as an Object but the difference is a Tile doesn't need to be moved so doesn't need to have input handled.
    Sorry that I wasn't very clear. I was not thinking that you should rename your Sprites to Movable. I was just thinking that you should create a Movable interface and have your Sprite class inherit that interface. Not knowing exactly what methods a "Movable" object would have, I made up my own:
    Code:
    class IMovable
    {
    public:
    	void MoveTo(struct Coordinates position) = 0;
    	struct Coordinates GetPosition() = 0;
    };
    That's probably over-simplifying. Maybe you would really have MoveLeft, MoveUp, MoveRight, and MoveDown methods. At any rate this is just an interface. You then need to implement it in your Sprite class:
    Code:
    class Sprite : public IMovable
    {
    private:
            struct Coordinates position;
    public:
            Sprite(struct Coordinates startPosition);
            ~Sprite();
        /* IMovable implementation */
            void MoveTo(struct Coordinates position);
            struct Coordinates GetPosition();
    };
    
    Sprite::Sprite(struct Coordinates startPosition)
    {
            this->position = startPosition;
    }
    
    Sprite::~Sprite()
    {
    }
    
    void Sprite::MoveTo(struct Coordinates position)
    {
            this->position = position;
    }
    
    struct Coordinates Sprite::GetPosition()
    {
            return this->position;
    }
    Then when you collect a bunch of objects that are movable, you check for their inheritance of the IMovable interface:
    Code:
    ...
    	std::vector<IMovable *> sprites;
    ...
    and you'll always know that the MoveTo and GetPosition methods will be part of those objects. This is how you program against an interface.

    Quote Originally Posted by \007 View Post
    I guess I was over complicating my designs?
    I don't think that you're over-complicating your designs. Just know that a simpler interface on the front usually requires more complex thinking and design patterns on the back.

    Quote Originally Posted by \007 View Post
    How do you help manage your designs while expanding your application? I pretty much hit the end of my initial design plans and now I am expanding things, in other words I am winging it now.
    Most of the time when I am expanding my application, if I need new things, maybe that I didn't think of before, I have need of refactoring quite a bit. Sometimes things have gotten too messy in the past, and I must virtually start over. It is always best to sit and think about designs and plan them out as much as possible before you touch a line of code. Also if you haven't studied design patterns, then I would suggest you do. There are several good books, and Wikipedia has a series on them. Not all of them are applicable everywhere, obviously. And try not to misuse them - some people misuse certain patterns especially. But overall, design patterns result in cleaner, more understandable, and more correct, performant code.
    Last edited by nathandelane; 04-05-2011 at 10:42 AM.

  5. #5
    Registered User \007's Avatar
    Join Date
    Dec 2010
    Posts
    179
    Thanks, I will play a bit with interfaces.

    I have heard of design patterns but haven't read much about them yet. I have read a bit of a design patterns book but at the time I hadn't done much OOP before so it was just for 'fun.' Since then I had forgotten about it-- looks like it is time to go back to design patterns and start to apply some.

    I think the hardest part of OOP is the designing-- then again you could probably say that about any programming method

  6. #6
    Registered User nathandelane's Avatar
    Join Date
    Dec 2010
    Posts
    9

    Post

    You're welcome. I'm glad I could help you somewhat, and hope that I can help in the future also. Just to be clear also, a true interface in C++ is a completely virtual class with no fields. For example:
    Code:
    class IRenderable
    {
    public:
    	virtual void render() = 0;
    	virtual bool isLoaded() = 0;
    };
    while a class with some virtual methods is technically an abstract class. Abstract classes must be inherited because they have some virtual methods that need to be implemented by a deriving class. For example:
    Code:
    class AbstractToken
    {
    private:
    	std::string _value;
    public:
    	AbstractToken(std::string value);
    	~AbstractToken();
    	std::string getValue();
    	
    	/* This must be implemented by a deriving class */
    	virtual bool tryParse(std::string value, AbstractToken * outToken) = 0;
    };
    
    AbstractToken::AbstractToken(std::string value)
    {
    	this->_value = value;
    }
    
    AbstractToken::~AbstractToken()
    {
    }
    
    std::string AbstractToken::getValue()
    {
    	return _value;
    }
    
    class ConcreteToken : public AbstractToken
    {
    public:
    	ConcreteToken(std::string value);
    	~ConcreteToken();
    
    	/* Here is the implementation for AbstractToken::tryParse */
    	bool tryParse(std::string value, AbstractToken * outToken);
    };
    
    ConcreteToken::ConcreteToken(std::string value) : AbstractToken(value)
    {
    }
    
    ConcreteToken::~ConcreteToken()
    {
    }
    
    bool ConcreteToken::tryParse(std::string value, AbstractToken * outToken)
    {
    	bool successful = true;
    
    	//...
    
    	return successful;
    }
    Both abstract classes and pure interfaces are considered interfaces in OOP. C++ is just a lot more loose on creating these than modern object-oriented programming languages. And it may not be that important, but it becomes more important when implementing design patterns.
    Last edited by nathandelane; 04-05-2011 at 01:38 PM.

  7. #7
    Registered User \007's Avatar
    Join Date
    Dec 2010
    Posts
    179
    Thanks-- I am still new to the idea of abstract classes. I am slowly gaining quite the appreciation for the standard library and how everything goes together so well. I hope I can make classes and have them work as good as they have done with the language itself.

    I have a lot of learning to do, haha.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Side-scroller Level Design
    By jmd15 in forum Game Programming
    Replies: 5
    Last Post: 04-25-2007, 02:48 PM
  2. Data Mapping and Moving Relationships
    By Mario F. in forum Tech Board
    Replies: 7
    Last Post: 12-14-2006, 10:32 AM
  3. OOP design question
    By codec in forum C++ Programming
    Replies: 3
    Last Post: 05-12-2004, 02:48 PM
  4. OOP Design
    By filler_bunny in forum C++ Programming
    Replies: 4
    Last Post: 09-07-2003, 05:37 AM
  5. design a game level
    By SuperNewbie in forum Windows Programming
    Replies: 3
    Last Post: 06-06-2002, 03:42 PM