What would be the best container mechanism for different game engine objects?

Currently, I'm working on my own game engine which uses SFML. Engine object types: Rectangle, Circle and Sprite uses the same base class which is called Object. Object class contains "name" variable and has virtual functions in it. Why they have same base class? Because I can store different types of classes in the same vector container (actually their Object pointer), and it would be easy to use their functions. Like:
engine.getObject("rec1")->move(3,0); //getObj... returns Object.
Mechanism worked fine, then I googled and saw an comment on some website which says "inhertiance way for engine objects is old 20 years and it's very slow". My final questions are: is this good way to do it and how other programs/games do objects mechanism? Where and how they store different types of objects?
And one more additional question. If inheritance way is good and if I should use std::vector for objects container. When I store rectangle's object pointer in vector, where should I store rectangle so it doesn't go out of scope? Should I use dynamic memory for it?
Btw. Happy new 2017 year! (Posted 12.30.2016)
Last edited on
I can't answer your question, but have you already read this? -> http://gameprogrammingpatterns.com/
Maybe it contains some answers to your questions (web version is free)
Last edited on
> Mechanism worked fine, then I googled and saw an comment on some website
> "inhertiance way for engine objects is old 20 years and it's very slow".
> My final questions are: is this good way to do it?

It may be more than twenty years old; but "very slow" may be more than fast enough for your particular requirement. As far as performance is concerned, one measurement is worth more than a thousand opinions.

Treat performance as a design constraint rather than as a design goal. I still remember this exchange with amusement:

> For a text file, the only portable way to do this is to read char by char, with escape sequence translations
> turned on, resizing the buffer as required, till end of file is reached. Doing this in C++ is painless.

>> 1. The above method is *incredibly* slow
>> and is exactly why C programmers tend to hate C++ programmers.

More of this delightful stuff, if you want to peruse it: http://www.cplusplus.com/forum/general/58900/

I was very new to this site at that time, this was my seventh or eight post; this closed account S6k9GNh0 (IIRC, the actual name was something like ComputerEquipped) was lording it over with some bazillions of posts. I was convinced that I had come to the wrong site, till I was reassured by the posts of Cubbi and m4ster r0shi.

As an aside, in my experience, C programmers who "hate C++ programmers", with a few notable exceptions, tend to be very poor C programmers.


> If inheritance way is good and if I should use std::vector for objects container.
> where should I store rectangle so it doesn't go out of scope?
> Should I use dynamic memory for it?

For a fast lookup based on name, a map or unordered_map with the key as the name of the object may be appropriate.

Using dynamic memory for the objects is not essential (for instance we could have array(s) of rectangle objects, array(s) of circle objects etc., with the map holding pointers to objects in these arrays). However, using dynamic memory may be more programmer efficient; then we wouldn't have to worry about synchronising the life-times of these arrays with the entries in he map. Perhaps use smart pointers to painlessly automate the management of life-time.

Something along these lines, perhaps:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
struct object
{
    virtual ~object() noexcept = default ;

    static std::size_t instance_count() noexcept { return objects.size() ; }

    void destroy() noexcept { objects.erase( name_ ) ; }
    static void destroy_all() noexcept { objects.clear() ; }

    virtual const std::string& name() const noexcept { return name_ ; }

    // other virtual functions eg.
    virtual void draw() const = 0 ;

    template < typename T, typename... OTHER_ARGS >
    static bool make_object( const std::string& name, OTHER_ARGS&&... args )
    { return objects.emplace( name, std::make_unique<T>( name, std::forward<OTHER_ARGS>(args)... ) ).second ; }

    static std::unique_ptr<object>& get( const std::string& name )
    {
        const auto iter = objects.find(name) ;
        if( iter == objects.end() ) throw std::invalid_argument( "no object named \"" + name + '"' ) ;
        else return iter->second ;
    }

    template < typename T > static T& get_as( const std::string& name )
    {
        const auto p = dynamic_cast<T*>( get(name).get() ) ;
        if( p == nullptr ) throw std::invalid_argument( "incorrect type" ) ;
        return *p ;
    }

    template< typename FN > static void for_each( FN&& fn )
    { for( auto& pair : objects ) std::forward<FN>(fn)( pair.second ) ; }

    protected: object( std::string name ) noexcept : name_( std::move(name) ) {}

    private:
        const std::string name_ ;

        // not copy assignable, not moveable
        object( object&& ) noexcept = delete ;
        object& operator= ( object&& ) noexcept = delete ;

        // map name of object -> pointer to object
        static std::unordered_map< std::string, std::unique_ptr<object> > objects ;
};

// in the implementation (.cpp) file
std::unordered_map< std::string, std::unique_ptr<object> > object::objects ;

http://coliru.stacked-crooked.com/a/fcae097b0076c23b
http://rextester.com/UWTJW88772
Thank you very much! It's really helpful.
Topic archived. No new replies allowed.