All pseudo code here, but you should reconsider your approach to the general design on the game engine. I apologize in advance, i started typing all this out and got bored by the end, if i skipped something and you would like some explanation, just ask. But it should all be self-explanatory.
You have a game, which contains a stack of scenes. Only the top most scene is updated, but any scene can be rendered. This makes it so if you have an in game menu, you can pause the game (by no longer updating the game scene) and render it as well as the menu on top. Makes it pretty easy. The scene has game objects, which are basically just glue objects containing components (Transforms, character controllers, ui's etc). Each component does something specific, with variables you can set at any time. You can retrieve game objects from the scene, and components from game objects. All objects are created by Game, as it is the factory. When game starts, it creates the main menu scene, and you just take it from there. This may seem a bit fuzzy or abstract, but the implementation is simple, consider the following:
Some class definitions
[source lang="cpp"]class Component() { enum Type { RENDERER, ENEMY, PLAYER, UI, ANIMATION, SCRIPT } Type m_eComponentType; virtual void Awake() = 0; // Called when component is created virtual void Destroy() = 0; // Called when component is destroyed virtual void Start() = 0; // Called when component is activated virtual void Stop() = 0; // Called when component is deactivated virtual void Update(float dt) = 0; // I'm not going to explain}class Renderer : public Component { int m_nTextureHandle; // All sorts of component specific members int m_nBufferId; // Look, we even have a buffer! void Awake(); // Overload void Destroy(); // Overload void Start(); // Overload void Stop(); // Overload void Update(float dt); // Overload void Render(); // Again, component specific!}class Enemy : public Component { // Two components, we're on fire! int m_nHealth; // Enemy specific junk AStar* m_pPathFinder; // Specific junk void Awake(); // Override void Destroy(); // Override void Start(); // Override void Stop(); // Override void Update(float dt); // Override}class Player : public Component { // See above void Awake(); void Destroy(); void Start(); void Stop(); void Update(float dt);}class UI : public Component { // See above void Awake(); void Destroy(); void Start(); void Stop(); void Update(float dt);}class GameObject() { // Now for the fun bit std::vector<Component*> m_vComponents; // A game object has components std::string m_strGameObjectName; // So we can retrieve game objects later bool m_bIsActive; // Does not always need to be active void GameObjectWasCreated(); // Created void GameObjectDidBecomeActive(); // Became active void GameObjectDidBecomeInactive(); // Became inactive void GameObjectWasDestroyed(); // Destroyed void AddComponent(Component*); void RemoveComponent(Component*); Component* GetComponent(Component::Type); void Update(float dt); void Render();}class Scene() { std::vector<GameObject*> m_vGameObjectList; // You can be more creative bool m_bRenderWhenNotActive; void SceneWasCreated(); void SceneDidBecomeActive(); void SceneDidBecomeInactive(); void SceneWasDestroyed(); void AddGameObject(GameObject*); void RemoveGameObject(GameObject*); GameObject* GetGameObject(std::string); void Update(float dt); void Render();}class Game { std::vector<Scene*> m_vSceneStack; bool Initialize(); // Initializes the game (Keyboard, mouse, managers, etc) void Shutdown(); // Destroys the game (blows away scene stack, game objects etc, shuts down managers) Scene* CreateNewScene(); // Creates a scene object GameObject* CreateGameObject(); // Creates a game object Component* CreateComponent(Component::Type); // Creates a component void DestroyScene(Scene*); // Delete the scene void DestroyGameObject(GameObject*); // Delete the game object void DestroyComponent(Component*); // Delete the component void Update(float dt); // Updates the top most object void Render();}[/source]
A little bit of implementation
[source lang="cpp"]void Renderer::Awake() { m_eComponentType = RENDERER; m_bIsActive = true; } // Do other stuff to set up your buffers and texturesvoid Renderer::Destroy() { } // Release your buffers and textures herevoid Renderer::Start() { } // Don't carevoid Renderer::Stop() { } // Don't carevoid Renderer::Update(float dt) { } // Don't carevoid Renderer::Render() { BindBuffer(m_nBufferId); BindTexture(m_nTextureId); RenderBatch(); }void Enemy::Awake() { m_eComponentType = ENEMY; m_bIsActive = true; } // Do other enemy stuff, like ai initialization and whatnotvoid Enemy::Destroy() { } // Shutdownvoid Enemy::Start() { if (m_pAStar == 0) m_pAStar = AStar::Factory::NewPathFinder(); } // I don't know, just arbitrary shitvoid Enemy::Stop() { if (m_pAStart != 0) AStar::Factory::Destroy(m_pAStar); m_pAStar = 0; } // Make sure you shut shit downvoid Enemy::Update(float dt) { m_pAStar->Find("Player"); AttackPlayerWhenPossible(); }// Be creative, make your player and ui components and junk!// For extra credit, make a script component, being able to script your engine will make life easy.class GameObject() { std::vector<Component*> m_vComponents; std::string m_strGameObjectName; bool m_bIsActive; void GameObject::GameObjectWasCreated() { m_bIsActive = true; } // Depending on your game, go crazyvoid GameObject::GameObjectDidBecomeActive() { // Let all the active components know, they are active again! for (int i = 0, size = m_vComponents.size(); i < size; ++i ) { if (m_vComponents[i].m_bIsActive) m_vComponents[i].Start(); }} void GameObject::GameObjectDidBecomeInactive() { // Active components, go away! for (int i = 0, size = m_vComponents.size(); i < size; ++i ) { if (m_vComponents[i].m_bIsActive) m_vComponents[i].Stop(); }} void GameObject::GameObjectWasDestroyed() { DestroyAllComponents(); } // No more game object, no more componentsvoid GameObject::AddComponent(Component* p) { // Make sure no other component of this type exists (be creative) m_vComponents.push_back(p); p->Awake(); p->Start();}void GameObject::RemoveComponent(Component* p) { // Make sure p is in component list // Get the index of the component p->Stop(); p->Destroy(); m_vComponents.erase(m_vComponents.begin() + componentIndex);}Component* GameObject::GetComponent(Component::Type t) { for (int i = 0; i < m_vComponents.size(); i < size; ++i) if (m_vComponents[i].m_eComponentType == t) // Remember, add component is only allowing one of each type return m_vComponents[i]; return 0;} void GameObject::Update(float dt) { // Propogate update to all components for (int i = 0; i < m_vComponents.size(); i < size; ++i) if (m_vComponents[i].m_bIsActive) m_vComponents[i].Update(dt);}void GameObject::Render() { // Only render render components for (int i = 0; i < m_vComponents.size(); i < size; ++i) if (m_vComponents[i].m_eComponentType == RENDERER && m_vComponents[i].m_bIsActive) ((RendererComponent*)m_vComponents[i])->Render();}void Scene::SceneWasCreated() { } // Do junk void Scene::SceneDidBecomeActive() { for (int i = 0, size = m_vGameObjectList.size(); i < size; ++i) if (m_vGameObjectList[i]->m_bIsActive) // Only active objects need to wake up m_vGameObjectList[i]->GameObjectDidBecomeActive();} void Scene::SceneDidBecomeInactive() { for (int i = 0, size = m_vGameObjectList.size(); i < size; ++i) if (m_vGameObjectList[i]->m_bIsActive) // Only active objects need to go to sleep m_vGameObjectList[i]->GameObjectDidBecomeActive();}void Scene::SceneWasDestroyed() { } // Do the opposite of create void Scene::AddGameObject(GameObject* p) { // Should not have two addresses of the same object in the list m_vGameObjectList.push_back(p); p->GameObjectWasCreated(); // Game object created in scene p->GameObjectDidBecomeActive(); // Game object is now active} void Scene::RemoveGameObject(GameObject* p) { // Make sure that p is in game object list p->GameObjectDidBecomeInactive(); // Game object no longer active p->GameObjectWasDestroyed(); // Game object is no longer in scene}GameObject* Scene::GetGameObject(std::string name) { // Retrieve first instance of game object (or null) based on name string} void Scene::Update(float dt) { for (int i = 0, size = m_vGameObjectList.size(); i < size; ++i) if (m_vGameObjectList[i]->m_bIsActive) // Only propogate to active game objects m_vGameObjectList[i]->GameObjectDidBecomeActive();} void Scene::Render() { for (int i = 0, size = m_vGameObjectList.size(); i < size; ++i) if (m_vGameObjectList[i]->m_bIsActive && m_vGameObjectList[i]->GetComponent(RENDERER) != 0) m_vGameObjectList[i]->Render(); // Don't need the above check, but figured i'd demonstrate}bool Game::Initialize() { Scene* mainMenu = CreateNewScene(); mainMenu->m_bRenderWhenNotActive = true; GameObject* mainMenuUIObject = CreateGameObject(); mainMenuUIObject->m_strGameObjectName = "MainMenuUI"; Component* mainMenuUIComponent = CreateComponent(UI); // Set all sorts of textures and junk for the main menu ui mainMenu->AddGameObject(mainMenuUIObject); mainMenuUIObject->AddComponent(mainMenuUIComponent); // You can make other scenes here, or inside of main menu, the point is // each new scene will be pushed on top of the scene stack. // How the stack may be used: // Push main menu, push audio options, set volume, pop audio options // Press play, pushes the game, now your playing // Exit to menu pops the game, now your back to the main menu}void Game::Shutdown() { // Tedious cleanup} Scene* Game::CreateNewScene() { return new Scene(); // You have a factory, be more creative} GameObject* Game::CreateGameObject() { return new GameObject(); // Same as above} Component* Game::CreateComponent(Component::Type t) { switch (t) { // Return appropriate component subclass }} void Game::DestroyScene(Scene* s) { delete s; // I'm getting bored of typing all this} void Game::DestroyGameObject(GameObject* p) { delete p;}void Game::DestroyComponent(Component* p) { delete p;} void Game::Update(float dt) { // Only update the top most scene m_vSceneStack[m_vSceneStack.size() - 1]->Update(dt);}void Game::Render() { // Render all renderable scenes // So, if you have the following stack // main menu > game > options // and game and options are both rendering, and options are opaque // It just looks like game is paused under options. for (int i = 0; i < m_vSceneStack.size() - 1; ++i) if (m_vSceneStack[i]->m_bRenderWhenNotActive) m_vSceneStack[i]->Render(); m_vSceneStack[m_vSceneStack.size() - 1]->Render();}[/source]
The limitations of this system are obvious, but you could easily make script components, and add a lot of power to your engine.
Hope this helps!
Edit, didn't see L.Spyro's response when i wrote this. Consider this the long version.
Edited by uglybdavis, 22 October 2012 - 08:01 PM.