Framework design fürs Spiel



  • Beitrag 13:21:15 30.01.2018 Titel: Anwendungsdesign in C++11/C++14 Zitieren
    Hallo,

    ich habe mir Gedanken gemacht zur einer "möglichen" Implementierung eines Frameworks für meine Anwendungen (GUI App, Spiel). Bislang bin ich aber mit dem Design nicht glücklich...und würde mal ganz gerne eure Kritik und /oder Anregungen hören. Das Design ist sehr stark an Doom 3 (BFG) angelehnt, aber meiner Meinung nach Objektorientierter. Hatte zuerst im falschen Forum gepostet, ich hoffe hier ist es richtig 😃

    ////////////////////////////////////////////////////////////////////////////// 
    class Foo 
    { 
    public: 
        virtual ~Foo() = default; 
    
    public: 
        virtual void Init() = 0; 
        virtual void Shutdown() = 0; 
    }; 
    
    class FooLocal: public Foo 
    { 
    public: 
        FooLocal(); 
    
    public: 
        virtual ~FooLocal(); 
    
    public: 
        virtual void Init(); 
        virtual void Shutdown(); 
    }; 
    
    FooLocal::FooLocal() 
    { 
        std::cout << "FooLocal ctor" << std::endl; 
    } 
    
    FooLocal::~FooLocal() 
    { 
        std::cout << "FooLocal dtor" << std::endl; 
    } 
    
    void FooLocal::Init() 
    { 
        std::cout << "FooLocal Init" << std::endl; 
    } 
    
    void FooLocal::Shutdown() 
    { 
        std::cout << "FooLocal Shutdown" << std::endl; 
    } 
    ////////////////////////////////////////////////////////////////////////////// 
    class Goo 
    { 
    public: 
        virtual ~Goo() = default; 
    
    public: 
        virtual void Init() = 0; 
        virtual void Shutdown() = 0; 
    }; 
    
    class GooLocal: public Goo 
    { 
    public: 
        GooLocal(); 
    
    public: 
        virtual ~GooLocal(); 
    
    public: 
        virtual void Init(); 
        virtual void Shutdown(); 
    }; 
    
    GooLocal::GooLocal() 
    { 
        std::cout << "GooLocal ctor" << std::endl; 
    } 
    
    GooLocal::~GooLocal() 
    { 
        std::cout << "GooLocal dtor" << std::endl; 
    } 
    
    void GooLocal::Init() 
    { 
        std::cout << "GooLocal Init" << std::endl; 
    } 
    
    void GooLocal::Shutdown() 
    { 
        std::cout << "GooLocal Shutdown" << std::endl; 
    } 
    ////////////////////////////////////////////////////////////////////////////// 
    class Boo 
    { 
    public: 
        virtual ~Boo() = default; 
    
    public: 
        virtual void Init() = 0; 
        virtual void Shutdown() = 0; 
    }; 
    
    class BooLocal: public Boo 
    { 
    public: 
        BooLocal(); 
    
    public: 
        virtual ~BooLocal(); 
    
    public: 
        virtual void Init(); 
        virtual void Shutdown(); 
    }; 
    
    BooLocal::BooLocal() 
    { 
        std::cout << "BooLocal ctor" << std::endl; 
    } 
    
    BooLocal::~BooLocal() 
    { 
        std::cout << "BooLocal dtor" << std::endl; 
    } 
    
    void BooLocal::Init() 
    { 
        std::cout << "BooLocal Init" << std::endl; 
    } 
    
    void BooLocal::Shutdown() 
    { 
        std::cout << "BooLocal Shutdown" << std::endl; 
    } 
    
    ////////////////////////////////////////////////////////////////////////////// 
    class Doo 
    { 
    public: 
        virtual ~Doo() = default; 
    
    public: 
        virtual void Init() = 0; 
        virtual void Shutdown() = 0; 
    }; 
    
    class DooLocal: public Doo 
    { 
    public: 
        DooLocal(); 
    
    public: 
        virtual ~DooLocal(); 
    
    public: 
        virtual void Init(); 
        virtual void Shutdown(); 
    }; 
    
    DooLocal::DooLocal() 
    { 
        std::cout << "DooLocal ctor" << std::endl; 
    } 
    
    DooLocal::~DooLocal() 
    { 
        std::cout << "DooLocal dtor" << std::endl; 
    } 
    
    void DooLocal::Init() 
    { 
        std::cout << "DooLocal Init" << std::endl; 
    } 
    
    void DooLocal::Shutdown() 
    { 
        std::cout << "DooLocal Shutdown" << std::endl; 
    } 
    ////////////////////////////////////////////////////////////////////////////// 
    class Moo 
    { 
    public: 
        virtual ~Moo() = default; 
    
    public: 
        virtual void Init() = 0; 
        virtual void Shutdown() = 0; 
    }; 
    
    class MooLocal: public Moo 
    { 
    public: 
        MooLocal(); 
    
    public: 
        virtual ~MooLocal(); 
    
    public: 
        virtual void Init(); 
        virtual void Shutdown(); 
    }; 
    
    MooLocal::MooLocal() 
    { 
        std::cout << "MooLocal ctor" << std::endl; 
    } 
    
    MooLocal::~MooLocal() 
    { 
        std::cout << "MooLocal dtor" << std::endl; 
    } 
    
    void MooLocal::Init() 
    { 
        std::cout << "MooLocal Init" << std::endl; 
    } 
    
    void MooLocal::Shutdown() 
    { 
        std::cout << "MooLocal Shutdown" << std::endl; 
    } 
    ////////////////////////////////////////////////////////////////////////////// 
    class Roo 
    { 
    public: 
        virtual ~Roo() = default; 
    
    public: 
        virtual void Init() = 0; 
        virtual void Shutdown() = 0; 
    }; 
    
    class RooLocal: public Roo 
    { 
    public: 
        RooLocal(); 
    
    public: 
        virtual ~RooLocal(); 
    
    public: 
        virtual void Init(); 
        virtual void Shutdown(); 
    }; 
    
    RooLocal::RooLocal() 
    { 
        std::cout << "RooLocal ctor" << std::endl; 
    } 
    
    RooLocal::~RooLocal() 
    { 
        std::cout << "RooLocal dtor" << std::endl; 
    } 
    
    void RooLocal::Init() 
    { 
        std::cout << "RooLocal Init" << std::endl; 
    } 
    
    void RooLocal::Shutdown() 
    { 
        std::cout << "RooLocal Shutdown" << std::endl; 
    } 
    ////////////////////////////////////////////////////////////////////////////// 
    // DLL??? 
    // statische Bibliothek???? 
    class Game 
    { 
    public: 
        virtual ~Game() = default; 
    
    public: 
        virtual void Init() = 0; 
    }; 
    
    namespace globals{ 
        extern Game *const game; 
    } 
    
    class GameLocal: public Game 
    { 
    public: 
        virtual ~GameLocal(); 
    
    public: 
        virtual void Init(); 
    }; 
    
    GameLocal::~GameLocal() 
    { 
        // dtor 
    } 
    
    void GameLocal::Init() 
    { 
        std::cout << "GameLocal Init" << std::endl; 
    } 
    
    namespace globals{ 
        extern GameLocal gamelocal; 
    } 
    ////////////////////////////////////////////////////////////////////////////// 
    // DLL??? 
    // statische Bibliothek???? 
    
    struct GameImport 
    { 
        int version; 
        Foo *foo; 
        Goo *goo; 
        Boo *boo; 
        Doo *doo; 
        Moo *moo; 
        Roo *roo; 
    }; 
    
    struct GameExport 
    { 
        int version; 
        Game *game; 
    }; 
    ////////////////////////////////////////////////////////////////////////////// 
    // Globale Funktionsdeklaration... 
    GameExport* GetGameApi(GameImport *const import); 
    
    //////////////////////////////////////////// 
    namespace{ 
        GameExport game_export; 
    
        Foo *foo {}; 
        Goo *goo {}; 
        Boo *boo {}; 
        Doo *doo {}; 
        Moo *moo {}; 
        Roo *roo {}; 
    
        Game *game {}; 
    } 
    
    // Globale Funktionsdefinition... 
    GameExport* GetGameApi(GameImport *const import) 
    { 
        foo = import->foo; 
        goo = import->goo; 
        boo = import->boo; 
        doo = import->doo; 
        moo = import->moo; 
        roo = import->roo; 
    
        game_export.game = globals::game; 
        return &game_export; 
    } 
    ////////////////////////////////////////////////////////////////////////////// 
    
    class App 
    { 
    public: 
        virtual ~App() = default; 
    
    public: 
        virtual void Init() = 0; 
        virtual void Run() = 0; 
        virtual void Shutdown() = 0; 
    }; 
    
    namespace globals{ 
        extern App *const app; 
    } 
    
    class AppLocal: public App 
    { 
    public: 
        AppLocal(); 
        AppLocal(const AppLocal &) = delete; 
        AppLocal& operator=(const AppLocal &) = delete; 
    
    public: 
        virtual ~AppLocal(); 
    
    public: 
        virtual void Init(); 
        virtual void Run(); 
        virtual void Shutdown(); 
    
    private: 
        // Subsysteme 
        FooLocal foo_; 
        GooLocal goo_; 
        BooLocal boo_; 
        DooLocal doo_; 
        MooLocal moo_; 
        RooLocal roo_; 
    }; 
    
    AppLocal::AppLocal(): 
        foo_ (), 
        goo_ (), 
        boo_ {}, 
        doo_ {}, 
        moo_ {}, 
        roo_ {} 
    { 
        std::cout << "AppLocal ctor"  << std::endl; 
    } 
    
    AppLocal::~AppLocal() 
    { 
        std::cout << "AppLocal dtor"  << std::endl; 
    } 
    
    void AppLocal::Init() 
    { 
        std::cout << "AppLocal Init"  << std::endl; 
    
        foo_.Init(); 
        goo_.Init(); 
        boo_.Init(); 
        doo_.Init(); 
        moo_.Init(); 
        roo_.Init(); 
    
        Foo* foo = &foo_; 
        Goo* goo = &goo_; 
        Boo* boo = &boo_; 
    
        GameImport game_import; 
        game_import.foo = foo; 
        game_import.boo = boo; 
        game_import.goo = goo; 
    
        GameExport* game_export = GetGameApi(&game_import); 
    
        game = game_export->game; 
    } 
    
    void AppLocal::Run() 
    { 
        std::cout << "AppLocal Run"  << std::endl; 
    } 
    
    void AppLocal::Shutdown() 
    { 
        // Exceptions... 
        foo_.Shutdown(); 
        goo_.Shutdown(); 
        boo_.Shutdown(); 
        doo_.Shutdown(); 
        moo_.Shutdown(); 
        roo_.Shutdown(); 
    } 
    ////////////////////////////////////////////////////////////////////////////// 
    
    namespace globals{ 
        extern AppLocal applocal; 
    } 
    
    ////////////////////////////////////////////////////////////////////////////// 
    
    namespace globals{ 
        AppLocal applocal; 
        App *const app = &applocal; 
    
        GameLocal gamelocal; 
        Game *const game = &gamelocal; 
    } 
    
    int WINAPI WinMain( 
                    HINSTANCE, 
                    HINSTANCE, 
                    LPSTR, 
                    int) 
    { 
        globals::app->Init(); 
        globals::app->Run(); 
        globals::app->Shutdown(); 
    
        return EXIT_SUCCESS; 
    }
    


  • Nee, gefällt mir nicht.



  • Oh je, mir auch nicht. Besser anders machen.



  • Wenigstens bekommt man hier mal Antworten mit denen man was anfangen kann 👍



  • Wenn deine Modul-Basisklassen (Foo, Goo, ...) alle denselben Aufbau haben, warum hast du dann nicht eine gemeinsame (abstrakte) Basisklasse? Dann könntest du die Module auch dynamisch in einer Liste verwalten und z.B. darüber iterieren etc.



  • unknown user schrieb:

    Das Design ist sehr stark an Doom 3 (BFG) angelehnt, aber meiner Meinung nach Objektorientierter.

    ohne es boese zu meinen, es schaut wie "c with classes" aus, nicht sonderlich objektorientiert.

    ...
    

    hast du das echt alles ausgeschrieben? oder copy&paste? grundsaetzlich, wenn du mehr als 2 zeilen dasselbe pattern siehst:

    for(auto& i:container)
       i.foo();
    

    das gilt im besonderen wenn du es copy&pastest ➡ don't!

    ich wuerde dir auch empfehlen nicht ein monster objekt zu generieren das alles als member hat was die welt zu bieten hat (AppLocal), weil dann alles beim starten von der laufzeitumgebung, ohne deine knotrolle, allokiert und initialisiert wird. das kann mit der zeit die startzeit deiner applikation verlaengern und zum anderen, wenn da irgendwo was schiefgeht, hast du kein handling fuer den fehler. Die "Init" funktion ist an sich schon gut, aber du wirst nicht garantieren dass irgend ein member vom member vom... nicht im ctor etwas anstellt, wodurch dann dein "framework" auf irgendeinem PC garnicht erst startet, vielleicht sogar bevor dein "logging" an ist etc.

    deswegen, scheu nicht ein "new" zu machen. DLLs zu laden verursacht auch allokationen, denen du nicht ausweichen kannst, deswegen ist ein "new foo" einmal in der ganzen programmlaufzeit zu verkraften.



  • rapso schrieb:

    unknown user schrieb:

    Das Design ist sehr stark an Doom 3 (BFG) angelehnt, aber meiner Meinung nach Objektorientierter.

    ohne es boese zu meinen, es schaut wie "c with classes" aus, nicht sonderlich objektorientiert.

    Jepp, eigentlich hatte besser machen wollen. Ich weiss auch das es Scheisse aussieht und nicht die Bohne OOP ist (außer ein ganz kleines bisschen).

    rapso schrieb:

    hast du das echt alles ausgeschrieben? oder copy&paste? grundsaetzlich, wenn du mehr als 2 zeilen dasselbe pattern siehst:

    for(auto& i:container)
       i.foo();
    

    das gilt im besonderen wenn du es copy&pastest ➡ don't!

    Ja, ich habe, besseren Wissens alles ausgeschrieben. 🙄

    Vielleicht brauche ich auch kein Application-Objekt sondern mich mehr auf die Subsysteme konzentrieren. Ich werde mir mal dazu ein Paar Gedanken machen 🙂

    Aber danke schon mal für die Antworten und Anregungen 👍



  • Das wäre ein möglicher grober Entwurf für das Anwendungsobjekt (mir geht es um die Parameter, die ich schon in den WinMain abgreifen muss):

    class AppTraits;
    
    class App
    {
    public:
        virtual ~App() = default;
    
    public:
        virtual void Defaults() = 0;
        virtual void Run() = 0;
        virtual void Setup() = 0;
        virtual AppTraits* GetTraits() = 0;
        virtual AppTraits* CreateTraits() = 0;
    };
    
    namespace global{
        extern App *app;
    }
    
    class AppTraits
    {
    public:
        HINSTANCE instance_exe {nullptr};
        std::string app_name {"Game 1.00"};
    };
    
    class AppLocal: public App
    {
    public:
        /*
            A "default constructor" is a constructor that
            can be called with no arguments (no parameters).
        */
        explicit AppLocal(/*AppTraits traits = AppTraits()*/);
        ~AppLocal();
    
    public:
        virtual void Defaults() final;
        virtual void Run() final;
        virtual void Setup();
        virtual AppTraits* GetTraits() final;
        virtual AppTraits* CreateTraits() final;
    
    private:
        void RestartVideoMode();
    
    private:
        // The order in which initializations will take place.
        std::unique_ptr<AppTraits> traits_;
        std::unique_ptr<FooLocal> foo_; 
        std::unique_ptr<GooLocal> goo_; 
        std::unique_ptr<BooLocal> boo_; 
        std::unique_ptr<DooLocal> doo_; 
        std::unique_ptr<MooLocal> moo_; 
        std::unique_ptr<RooLocal> roo_; 
    };
    
    AppLocal::AppLocal():
        traits_(),
        foo_{std::make_unique<FooLocal>()},
        goo_{std::make_unique<GooLocal>()},
        boo_{std::make_unique<BooLocal>()},
        doo_{std::make_unique<DooLocal>()},
        moo_{std::make_unique<MooLocal>()},
        roo_{std::make_unique<RooLocal>()}
    {
        global::app = this;
        global::applocal = this;
    }
    
    AppLocal::~AppLocal()
    {
        global::app = nullptr;
        global::applocal = nullptr;
    }
    
    void AppLocal::Defaults()
    {
        foo_ = std::make_unique<FooLocal>();
        goo_ = std::make_unique<GooLocal>();
        boo_ = std::make_unique<BooLocal>();
        doo_ = std::make_unique<DooLocal>();
        moo_ = std::make_unique<MooLocal>();
        roo_ = std::make_unique<RooLocal>();
    }
    
    AppTraits* AppLocal::GetTraits()
    {
        return traits_.get();
    }
    
    AppTraits* AppLocal::CreateTraits()
    {
        if(!traits_){
            traits_ = std::make_unique<AppTraits>();
        }
    
        return traits_.get();
    }
    
    void AppLocal::Setup()
    {
    
    }
    

    Die globalen Zeiger brauche ich z.B. für die Fensterprozedur.

    Die AppTraits können so an verschiedene Systeme angepasst werden. Hier an Windows. Allerdings kann ich nur dann den Standardkonstruktor verwenden und benötige daher eine Setup Funktion.

    Das bedeutet, das ich einen Konstruktor und eine "Init"-Funktion bzw. Setup Funktion benötige. Das ist alles so wurstig... 😡

    Viele Frameworks machen das allerdings ähnlich.

    Gruß